Add hierarhical managment
33
backend/wordpress/wp-content/plugins/groups/COPYRIGHT.txt
Normal file
@@ -0,0 +1,33 @@
|
||||
|
||||
Groups
|
||||
|
||||
Copyright 2011-2018 "kento" (Karim Rahimpur) www.itthinx.com
|
||||
|
||||
The files COPYRIGHT.txt and LICENSE.txt as well as ALL NOTICES IN THE
|
||||
HEADERS OF ALL FILES MUST BE KEPT INTACT.
|
||||
|
||||
GPL-licensed
|
||||
|
||||
Unless otherwise stated, all code in this plugin is licensed under
|
||||
the GPL License:
|
||||
|
||||
All code is part of this plugin, hereinafter referred to as "program".
|
||||
|
||||
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/>.
|
||||
|
||||
|
||||
Visit http://www.itthinx.com/ for help, general info and fun.
|
||||
|
||||
Plugin page: http://www.itthinx.com/plugins/groups
|
||||
|
||||
674
backend/wordpress/wp-content/plugins/groups/LICENSE.txt
Normal 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:
|
||||
|
||||
<program> Copyright (C) <year> <name of author>
|
||||
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>.
|
||||
465
backend/wordpress/wp-content/plugins/groups/changelog.txt
Normal file
@@ -0,0 +1,465 @@
|
||||
== Groups by itthinx - changelog.txt ==
|
||||
|
||||
= 2.3.1 =
|
||||
* Fixed several points where warnings related to the use of count() and the Countable interface would occur due to changes in PHP 7.2.
|
||||
* Tested with WordPress 4.9.
|
||||
* Added the groups_post_access_posts_where_query_get_post_types filter to allow additional processing to cover for cases like where
|
||||
wc_query is set.
|
||||
* Fixed a notice related to the use of an undefined variable in the Groups_Post_Access class.
|
||||
* Added the logo and the option to get reminded later to the Groups notice.
|
||||
* Added filters on get_previous_post_where and get_next_post_where to restrict access on adjacent posts.
|
||||
|
||||
= 2.3.0 =
|
||||
* Tested with WordPress 4.8.
|
||||
* Fixed a REST API access restriction issue, added the filter rest_prepare_{$post_type} to
|
||||
grant or deny access to individual posts.
|
||||
* Updated the translation template.
|
||||
* German translation updated.
|
||||
* Updated the appearance of the network settings.
|
||||
* Updated the table creation process dropping use of dbDelta() due to its restrictions (can't handle IF NOT EXISTS).
|
||||
* Fixed an issue related to cache and switching to a blog when neither wp_cache_switch_to_blog() nor wp_cache_reset()
|
||||
are implemented, like in WP Engine's object-cache.php which does provide wp_cache_flush().
|
||||
* Guarded against concurrent execution of multiple instances of plugin activation and deactivation processes.
|
||||
* Fixed an attempt to refresh capabilities when no previous version was installed.
|
||||
|
||||
= 2.2.0 =
|
||||
* Important change in this version: If access restrictions for post type are disabled, related entries will not be protected anymore.
|
||||
* Improved the activation performance by simplifying the creation of user-group entries.
|
||||
The 'groups_created_user_group' action is not invoked on incorporating existing users and
|
||||
a single query is used to create entries for all users.
|
||||
Based on suggestions from @haroldkyle in https://github.com/itthinx/groups/pull/14
|
||||
* Added a warning message displayed in the plugins list when Groups is going to delete its
|
||||
data on deactivation.
|
||||
* Added the ability to obtain user_ids from Groups_Group.
|
||||
* Improved the performance when obtaining users from Groups_Group.
|
||||
Based on suggestions from @tricki in https://github.com/itthinx/groups/pull/50
|
||||
* Adjusted the signature of the Groups_User constructor to use null as default for its $user_id argument.
|
||||
* Added methods in Groups_Post_Access to get and set which post types Groups should handle.
|
||||
* Changed the default assumption to handle only post types that are 'public' by default. Others are not handled by default.
|
||||
* Updated the Options to show all available post types.
|
||||
* Improved performance by limiting our posts_where, wp_count_posts and other filters in Groups_Post_Access to handled post types only.
|
||||
* Fixed a potential fatal error when the user_register hook is fired and get_current_screen() is not defined.
|
||||
* Updated translations.
|
||||
* Fixed a notice when no post types are selected in Groups > Options.
|
||||
* Added comment filters to hide comments on protected entries.
|
||||
* Improved the performance of the post filter.
|
||||
* Added several performance improvements related to data retrieval and caching on various objects and data sets.
|
||||
* Added a notice handler class.
|
||||
|
||||
= 2.1.2 =
|
||||
* Fixed a warning that came up when the post type in a query is provided as an array indicating multiple post types.
|
||||
* Users who can administer Groups (with the groups_admin_groups capability) now also see posts restricted to groups
|
||||
they do not belong to, in line with the ability to restrict access with groups they do not belong to for consistency's
|
||||
sake.
|
||||
* Added a filter on woocommerce_product_is_visible so protected up-sell and cross-sell products
|
||||
are effectively hidden.
|
||||
|
||||
= 2.1.1 =
|
||||
* Changed the default value for legacy mode used on installation to false. Fixes database errors
|
||||
due to missing capability table at that stage.
|
||||
* Modified the method signature of Groups_Post_Access::posts_where() and
|
||||
Groups_Post_Access_Legacy::posts_where() to avoid PHP 7.1 warnings (reference expected, value given).
|
||||
* Removed the administrator override option on the back end. Administrator override now requires the constant
|
||||
GROUPS_ADMINISTRATOR_OVERRIDE to be defined as true.
|
||||
* Updated the French translation.
|
||||
* Adjusted the load order for translations.
|
||||
|
||||
= 2.1.0 =
|
||||
* Changed the requirements to allow to restrict by group.
|
||||
* Fixed legacy access restrictions help replaced new groups help.
|
||||
|
||||
= 2.0.3 =
|
||||
* Fixed a "Fatal error: Call to a member function get_role_caps() on a non-object" which
|
||||
could occur in circumstances with invalid user IDs.
|
||||
|
||||
= 2.0.2 =
|
||||
* Added the option to dismiss the Welcome screen.
|
||||
* Updated German and Spanish translations.
|
||||
|
||||
= 2.0.1 =
|
||||
* Fixed an issue with conflicting cache entries with legacy mode enabled.
|
||||
* Fixed access restriction capabilities were reset when reenabling legacy mode.
|
||||
|
||||
= 2.0.0 =
|
||||
* Changed the access restriction model to group-based access restrictions.
|
||||
* Provides an optional legacy mode that supports the previous capability-based access restrictions.
|
||||
* Optimized the approach to restrict read access on posts using groups rather than access restriction capabilities.
|
||||
* Added the `groups_restrict_access` capability which grants users to impose access restrictions.
|
||||
* Fixed the order_by and order parameters in Groups_Group::get_groups()
|
||||
* Added the $create parameter in Groups_UIE::render_select()
|
||||
* Removed the "Chosen" library.
|
||||
* Improved and reduced the footprint on the Users admin screen, now allowing to filter by one or multiple groups.
|
||||
* Added a welcome screen.
|
||||
* Added the option to assign groups to new users directly when creating them from the Dashboard.
|
||||
* Changed the language domain to string literal instead of constant.
|
||||
* Improvements on internal coding standards (single/double quotes, EOF, formatting).
|
||||
|
||||
= 1.13.1 =
|
||||
* Fixed an issue where the wrong post count would be produced for statuses that do not represent a valid WooCommerce order status.
|
||||
* Added support for post counts with WooCommerce Subscriptions.
|
||||
|
||||
= 1.13.0 =
|
||||
* Added a filter on wp_count_posts.
|
||||
|
||||
= 1.12.0 =
|
||||
* Fixed an issue where resource paths were not correct with customized WP_CONTENT_DIR, WP_CONTENT_URL, WP_PLUGIN_DIR and WP_PLUGIN_URL.
|
||||
|
||||
= 1.11.3 =
|
||||
* Updated the Spanish translation.
|
||||
|
||||
= 1.11.2 =
|
||||
* Updated the translation template.
|
||||
* Updated the German translation.
|
||||
* Moved the complete changelog out to changelog.txt.
|
||||
|
||||
= 1.11.1 =
|
||||
* Fixed the order_by and order parameters in Groups_Group::get_groups()
|
||||
|
||||
= 1.11.0 =
|
||||
* Added the Brazilian Portuguese translation.
|
||||
|
||||
= 1.10.3 =
|
||||
* Added the groups_access_restrictions_capability_selected filter.
|
||||
|
||||
= 1.10.2 =
|
||||
* Tested for WordPress 4.5.
|
||||
|
||||
= 1.10.1 =
|
||||
* Fixed the French translation which introduced incorrect markup breaking the help button.
|
||||
* Added the groups_admin_groups_add_form_after_fields filter.
|
||||
* Added the groups_admin_groups_add_submit_success action.
|
||||
* Added the groups_admin_groups_edit_form_after_fields filter.
|
||||
* Added the groups_admin_groups_edit_submit_success action.
|
||||
* Fixed some code formatting (tabs).
|
||||
|
||||
= 1.10.0 =
|
||||
* Adjusted the Groups and Capabilities screens' UI elements.
|
||||
* Reduced the filters section footprint on the Groups screen.
|
||||
* Reduced the filters section footprint on the Capabilities screen.
|
||||
* Fixed use of deprecated constructor in the Groups_Pagination class.
|
||||
* Fixed sorting by description and the sorting indicator on the Groups screen.
|
||||
* Fixed sorting by description and the sorting indicator on the Capabilities screen.
|
||||
* Fixed some code formatting issues including superfluous whitespace.
|
||||
* Improved the footer rendering and footprint.
|
||||
|
||||
= 1.9.1 =
|
||||
* WordPress 4.4 compatible.
|
||||
* Updated selectize.js to 0.12.1 (standalone).
|
||||
* Fixed a representation issue for the lower group selector on the Users screen.
|
||||
* Updated the page headings to use h1 instead of h2.
|
||||
* Added the is_member method to Groups_User.
|
||||
* Updated the footer.
|
||||
|
||||
= 1.9.0 =
|
||||
* Added own cache encapsulation to guard against flaws in outdated cache implementations.
|
||||
* Added new user group property to obtain hierarchical groups.
|
||||
* Improved user properties to use own caching.
|
||||
* Fixed media field filters wrongly declared as actions.
|
||||
* Fixed the version comparison operation.
|
||||
|
||||
= 1.8.1 =
|
||||
* Fixed potential XSS vulnerabilities related to the unescaped use of the $_SERVER['REQUEST_URI'] in some forms.
|
||||
|
||||
= 1.8.0 =
|
||||
* WordPress 4.3.1 compatibility tested.
|
||||
* Add-ons section added.
|
||||
|
||||
= 1.7.2 =
|
||||
* WordPress 4.3 compatibility tested.
|
||||
* Updated the menu position constant (string instead of number).
|
||||
* Removed translation of the Groups menu title (related to a consistent appearance and would also be affected by a a core bug).
|
||||
* Fixed padding for the header checkbox on the Groups and Capabilities screens.
|
||||
|
||||
= 1.7.1 =
|
||||
* Fixes an issue with map_meta_cap filtering where no valid post ID is provided.
|
||||
|
||||
= 1.7.0 =
|
||||
* Added the French translation.
|
||||
* Added the [groups_login] shortcode.
|
||||
* Added the [groups_logout] shortcode.
|
||||
* Updated the German translation.
|
||||
* Updated the Spanish translation.
|
||||
* Added the groups_deleted_capability_capability action.
|
||||
* Fixed an issue with deleted capabilities restricting access to posts.
|
||||
* Fixed cache entries for capabilities.
|
||||
|
||||
= 1.6.0 =
|
||||
* Added the German translation.
|
||||
* Updated the Spanish translation.
|
||||
* Updated the Groups menu position.
|
||||
* Removed empty strings from translation.
|
||||
|
||||
= 1.5.5 =
|
||||
* Added administrative links to the plugin entry.
|
||||
|
||||
= 1.5.4 =
|
||||
* Added the Dutch translation.
|
||||
|
||||
= 1.5.3 =
|
||||
* Added a comparison method for groups and capabilities.
|
||||
* Updated the documentation link in the help content.
|
||||
|
||||
= 1.5.2 =
|
||||
* Improved internal definitions to use API function instead of WP_CONTENT_DIR
|
||||
and WP_CONTENT_URL constants.
|
||||
* Now showing inherited capabilities for groups.
|
||||
* Added ABSPATH check to plugin main file.
|
||||
* Improved the UI rendering cancel links as buttons.
|
||||
* Improved the UI adding some space on capability selector box.
|
||||
* Fixed a pagination issue when the page number is indicated on the Groups or Capabilities screen.
|
||||
|
||||
= 1.5.1 =
|
||||
* Please **MAKE A BACKUP** of your site and database PRIOR to updating.
|
||||
* WordPress 4.2 compatible.
|
||||
* Adopted a more flexible index size on the capability row of the capability table.
|
||||
|
||||
= 1.5.0 =
|
||||
* Please **MAKE A BACKUP** of your site and database PRIOR to updating.
|
||||
* WordPress 4.2 compatible.
|
||||
* Reduced the index size on the capability row of the capability table.
|
||||
|
||||
= 1.4.15 =
|
||||
* Due to changes in versions 1.4.14 and 1.4.15, it's important to **MAKE A BACKUP** of the site & database, test the site, extensions & theme PRIOR to updating.
|
||||
* Fixes a cache incompatibility with caching mechanisms that do not implement wp_cache_get()'s function signature fully.
|
||||
This addresses cases specifically where the fourth parameter $found is not initialized as expected upon return.
|
||||
The performance improvements included in this release are lessened with caching plugins that fail to implement the return value disambiguation via $found.
|
||||
|
||||
= 1.4.14 =
|
||||
* Now not using Groups' the_posts filter by default as results are already filtered by Groups' posts_where filter.
|
||||
* Added the groups_filter_the_posts filter which can be used to 'reactivate' Groups' the_posts filter where needed.
|
||||
* Added caching for capabilities read by capability name.
|
||||
* Added caching for groups read by name.
|
||||
* Added caching for results obtained in Groups_Post_Access::user_can_read_post(...).
|
||||
* Added the groups_post_access_user_can_read_post filter.
|
||||
* Admin override is disabled by default (existing installs need to disable manually if options were saved).
|
||||
* Swedish translation by [Andréas Lundgren](http://adevade.com) added.
|
||||
|
||||
= 1.4.13 =
|
||||
* WordPress 4.1 compatible.
|
||||
|
||||
= 1.4.12 =
|
||||
* Fixes missing selectize Javascript for the media uploader's attachment popup.
|
||||
|
||||
= 1.4.11 =
|
||||
* WordPress 4.0 compatible.
|
||||
|
||||
= 1.4.10 =
|
||||
* Improved: code documentation
|
||||
* WordPress 3.9 compatibility checked
|
||||
* Changed some filter usage with prepare() for 3.9 nags.
|
||||
* Fixed unmatched tags in the tree view.
|
||||
|
||||
= 1.4.9 =
|
||||
* Fixed: Tree view doesn't appear/disappear in menu directly after setting the option.
|
||||
* Improved: Feedback when options have been saved.
|
||||
* Improved: UI size adjustments.
|
||||
* Added: New API methods Groups_Group::get_group_ids() and Groups_Group::get_groups().
|
||||
* Improved: groups and capabilities table cell titles and ellipsis added.
|
||||
|
||||
= 1.4.8 =
|
||||
* Fixed: A closing tag in the group list on the user profile.
|
||||
* Fixed: Help wording.
|
||||
* Improved: Capabilities in the Access Restrictions column are sorted for more consistent display.
|
||||
* Improved: Reduced ID, Edit and Remove column widths on Groups and Capabilities screens.
|
||||
* Fixed: Stripping added slashes from groups and capabilities displayed.
|
||||
* Added: Feedback when groups and capabilities are created, updated or removed in admin.
|
||||
* Added: group and exclude_group attributes for the [groups_user_groups] shortcode.
|
||||
* Improved: Replaced remnant CR LF line-endings in code.
|
||||
* Fixed: Handling updates to a capability when the capability field is empty.
|
||||
* Fixed: Handling updates to a group when the name field is empty.
|
||||
* Fixed: Don't allow to use the name of another existing group when updating a group.
|
||||
* Fixed: Don't allow to use the name of another existing capability when updating one.
|
||||
|
||||
= 1.4.7 =
|
||||
* Security improvement: plugin files accessed directly exit
|
||||
|
||||
= 1.4.6.1 =
|
||||
* Fixed: Don't interfere with output when there is no post (the_content and get_the_excerpt filters)
|
||||
|
||||
= 1.4.6 =
|
||||
* Security fix : Certain capabilities could be granted to users instead of being denied with a change introduced in version 1.4.5. Roles with negated capabilities would effectively grant these capabilities to the user.
|
||||
|
||||
= 1.4.5 =
|
||||
* Using a WordPress API function get_post_type_capabilities() instead of semi-hardcoded capabilities for access restriction checks (affects CPTs).
|
||||
* Changed: Taking role-based capabilities into account when creating cache entries for the Groups_User object. The new groups_user_add_role_capabilities filter allows to modify this new behaviour by returning false.
|
||||
* Added: groups_user_add_role_capabilities filter.
|
||||
|
||||
= 1.4.4 =
|
||||
* WordPress 3.8 compatibility checked.
|
||||
* Fixed: Access restriction options per post type when none is checked.
|
||||
|
||||
= 1.4.3 =
|
||||
* Added: Bulk editing (add/remove) of post access restriction capabilities.
|
||||
* Fixed: A typo in the Access Restriction column's tooltip text.
|
||||
* Fixed: Validation of access restriction capabilities when saved on options admin screen.
|
||||
* Changed: Users must now have the groups_access capability to be able to use the access restriction meta box on posts.
|
||||
|
||||
= 1.4.2 =
|
||||
* Added: Access restriction capabilities shown for enabled post types on overview screens.
|
||||
* WordPress 3.7.1 compatibility checked.
|
||||
* Fixed: Error caused by typo when obtaining group_ids_deep property for a Groups_User.
|
||||
* Changed: Replaced some __get calls by properties.
|
||||
* Added: Filter by access restriction capabilities for enabled post types on overview screens.
|
||||
|
||||
= 1.4.1 =
|
||||
* Added: Better group-assignment on the Users admin screen, allows to assign/remove multiple users to/from multiple groups along with a better UI.
|
||||
* Changed: Groups requires at least WordPress 3.5 now, although this only affects the group-action functionality on the Users admin screen, the restrict_manage_users action which is now used to render the UI elements needed, was introduced with WordPress 3.5.
|
||||
* Added: Extensions box in Options.
|
||||
* Improved: Groups section in user profile with added description.
|
||||
|
||||
= 1.4.0 =
|
||||
* Added: Groups > Groups > Add / Edit group screens, allow to assign/modify the capabilities assigned to the group.
|
||||
* Added: Groups > Groups screen, allow to assign/remove multiple capabilities to multiple groups.
|
||||
* Added: Groups > Groups screen, allow to delete multiple groups as a bulk action.
|
||||
* Added: Groups > Capabilities screen, allow to delete multiple capabilities as a bulk action.
|
||||
* Improved: Groups > Options screen, using searchable select instead of checkboxes to enable capabilities for access restriction.
|
||||
* Improved: In user profiles, using a searchable select to modify group assignments.
|
||||
* Improved: Reduced the footer text in groups admin sections.
|
||||
* Improved: Admin CSS to make better use of screen real-estate and more coherent appearance with the new UI additions.
|
||||
|
||||
= 1.3.14 =
|
||||
* Added the option to quick-create group and capability within the access restriction meta-box.
|
||||
* Added the option to show groups granting access per capability in the access restriction meta-box.
|
||||
* Added the quick-create field to the access restrictions meta-box which allows to create group & capability on the fly.
|
||||
* Added [Selectize.js](http://brianreavis.github.io/selectize.js/) and using it in the access restrictions meta-box instead of checkboxes.
|
||||
* Improved the Groups > Options screen using a Selectize-based selection of capabilities that are enabled for access restriction.
|
||||
|
||||
= 1.3.13 =
|
||||
* Fixed duplicate postmeta created when saving access restriction capabilities for a post.
|
||||
* [groups_can] and [groups_can_not] now accept multiple capabilities separated by comma.
|
||||
* WordPress 3.6.1 compatibility checked.
|
||||
|
||||
= 1.3.12 =
|
||||
* WordPress 3.6 compatibility checked.
|
||||
* Fixed table appearance for capabilities and groups admin sections when there are no results.
|
||||
|
||||
= 1.3.11 =
|
||||
* Fix: Access restriction capabilities must be disjunctive.
|
||||
* Added: List of groups can be shown in user profiles on the back end and group assignments can be edited by group admins.
|
||||
* Improvement: Groups shown for users on the Users screen are sorted by name.
|
||||
|
||||
= 1.3.10 =
|
||||
* Fix: Under certain conditions with caching involved, capabilities were not correctly retrieved. Thanks to Jason Kadlec who [reported the issue](http://wordpress.org/support/topic/nasty-error-with-latest-version).
|
||||
* Improvement: Related to the above fix, improved the way how *_deep properties are retrieved on cache misses, resulting in slightly better performance.
|
||||
* Fix: Added a missing text domain.
|
||||
* Improvement: Added help icon when user has no access restriction capabilities.
|
||||
* Fix: Redirecting after group action in users screen to end up with a clean admin URL.
|
||||
|
||||
= 1.3.9 =
|
||||
* Fix: added filter hooked on posts_where motivated by pagination issues - the posts must be filtered before the totals are calculated in WP_Query::get_posts().
|
||||
* Improvement: modified the signature of the the_posts filter method in Groups_Post_Access to receive the $query by reference
|
||||
* Improvement: a substantial improvement on overall performance is achieved by caching user capabilities and groups
|
||||
* Fix: access restriction boxes showing capabilities that the user should not be allowed to set to restrict posts
|
||||
* Fix: resolve user-capability when a capability is deleted
|
||||
|
||||
= 1.3.8 =
|
||||
* Fix: using substitute wp_cache_switch_to_blog instead of deprecated function wp_cache_reset when available (from 3.5.0)
|
||||
* Fix: don't show access restriction meta box on attachments, the option is added with the attachment fields (3.5 uses common post edit screen but save_post isn't triggered on attachments)
|
||||
* Improvement: limiting choice of access restrictions to those the current user has
|
||||
* Fix: restrict access to edit or delete posts based on the post's access restrictions
|
||||
* Feature: added option to refresh capabilities
|
||||
* Fix: replaced use of get_user_by() (memory leaks on large user sets) with query & added batch limit when adding users to Registered group on activation
|
||||
|
||||
= 1.3.7 =
|
||||
* Fix: missing argument for meta box when saving a post
|
||||
* Fix: Groups conflicting with other plugins adding columns to the Users screen (in the manage_users_custom_column filter) thanks to [Erwin](http://www.transpontine.com) who spotted this :)
|
||||
|
||||
= 1.3.6 =
|
||||
* Replaced call to get_users() with query to avoid memory errors on activation with large users bases.
|
||||
* Provided a default value for a method in Groups_Access_Meta_Boxes to avoid issues with other plugins or themes.
|
||||
|
||||
= 1.3.5 =
|
||||
* Fixed out of memory issues with large user bases on Users > All Users page. Thanks to [Jason Glaspey](http://www.jasonglaspey.com) who spotted the issue :)
|
||||
|
||||
= 1.3.4 =
|
||||
* WP 3.5 cosmetics
|
||||
|
||||
= 1.3.3 =
|
||||
* WP 3.5 compatibility http://core.trac.wordpress.org/ticket/22262
|
||||
|
||||
= 1.3.2 =
|
||||
* Fixed capabilities cannot be added or removed from groups in localized installations
|
||||
|
||||
= 1.3.1 =
|
||||
* Added users property to Groups_Group
|
||||
* Moved tests out of core folder
|
||||
* Fixed missing $wpdb in Groups_Group's getter
|
||||
* Added group filters on users admin section
|
||||
|
||||
= 1.3.0 =
|
||||
* Added feature that allows to show access restrictions depending on post type
|
||||
* Added support for access restrictions on Media
|
||||
* Fixed issue, removed access restrictions offered on Links
|
||||
|
||||
= 1.2.5 =
|
||||
* Added Spanish translation
|
||||
|
||||
= 1.2.4 =
|
||||
* Minor improvements on Options screen
|
||||
* Added show="users" option to [groups_group_info] shortcode which lists user logins for users in a group - rather experimental as it doesn't offer any sorting, pagination, linking or other options
|
||||
|
||||
= 1.2.3 =
|
||||
* New shortcode [groups_join group="..."] lets a user join the given group
|
||||
* New shortcode [groups_leave group="..."] lets a user leave the given group
|
||||
|
||||
= 1.2.2 =
|
||||
* Revised styles
|
||||
* WordPress 3.4 compatibility
|
||||
* Dropping support for WordPress < 3.3
|
||||
* Help uncluttered.
|
||||
|
||||
= 1.2.1 =
|
||||
* Reduced files loaded on non-admin pages.
|
||||
* Added Lithuanian translation.
|
||||
* Changed help to use tabs.
|
||||
|
||||
= 1.2.0 =
|
||||
* Access control is no longer restricted to the groups_read_post capability: now any capability can be used to limit access to posts so that different groups can be granted access to different sets of posts.
|
||||
|
||||
= 1.1.5 =
|
||||
* Added shortcode & API functions [groups_user_group] / [groups_user_groups] that allows to show the list of groups the current user or a specific user belongs to
|
||||
* Added shortcode & API functions [groups_groups]to show the site's list of groups
|
||||
* Class comments.
|
||||
|
||||
= 1.1.4 =
|
||||
* Reduced plugin admin footer.
|
||||
|
||||
= 1.1.3 =
|
||||
* Added safety & warning to test page.
|
||||
|
||||
= 1.1.2 =
|
||||
* Tested on WP 3.3.2
|
||||
|
||||
= 1.1.1 =
|
||||
* Multisite: Fixed (removed) conditions that would only make Groups act on public and non-mature sites
|
||||
* Multisite: Adding add/remove to group only on sites', not network users admin screen
|
||||
* Multisite: Added constraint in user_register hook checking if the user is a member of the blog
|
||||
|
||||
= 1.1.0 =
|
||||
* Added Groups menu to network admin
|
||||
* Added option to delete plugin data for all sites on multisite installations; removed option for individual sites
|
||||
* Improved activation and deactivation for network installs
|
||||
* Increases column sizes on capabilities table and fixes cut-off capabilities delete_published_pages and delete_published_posts
|
||||
|
||||
= 1.0.0-beta-3d =
|
||||
* Fixed issues caused by an excessively long index for the capability DB table.
|
||||
Some installations wouldn't work correctly, showing no capabilities and making it impossible to add new ones.
|
||||
* Taking into account blog charset/collation on newly created tables.
|
||||
|
||||
= 1.0.0-beta-3c =
|
||||
* Groups shortcodes now allow nesting.
|
||||
|
||||
= 1.0.0-beta-3b =
|
||||
* Fixed admin override option not being updated
|
||||
* DB tables checked individually to create (motivated by case of all but capability table not being created)
|
||||
|
||||
= 1.0.0-beta-3 =
|
||||
* Groups wouldn't activate due to a fatal error on WP <= 3.2.1 : is_user_member_of_blog() is defined in ms-functions.php
|
||||
* Added [groups_group_info] shortcode
|
||||
|
||||
= 1.0.0-beta-2 =
|
||||
* Increased length of capability.capability, capability.class, capability.object columns to support long capabilities.
|
||||
* Improved admin CSS.
|
||||
|
||||
= 1.0.0-beta-1 =
|
||||
* This is the first public beta release.
|
||||
19
backend/wordpress/wp-content/plugins/groups/css/groups.css
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* groups.css
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.0.0
|
||||
*/
|
||||
389
backend/wordpress/wp-content/plugins/groups/css/groups_admin.css
Normal file
@@ -0,0 +1,389 @@
|
||||
/**
|
||||
* groups_admin.css
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.0.0
|
||||
*/
|
||||
.manage-groups,
|
||||
.manage-capabilities {
|
||||
margin-right: 1em;
|
||||
}
|
||||
.filters,
|
||||
.manage {
|
||||
background-color: #fff;
|
||||
padding: 0.62em;
|
||||
margin-bottom: 0.62em;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
.filters fieldset,
|
||||
.manage fieldset {
|
||||
display: inline-block;
|
||||
clear: both;
|
||||
}
|
||||
.filters legend {
|
||||
padding: 0 0 2px 0;
|
||||
}
|
||||
.filters form label {
|
||||
white-space: nowrap;
|
||||
margin-bottom: 0.62em;
|
||||
}
|
||||
.manage a.add {
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
.manage a.add .icon {
|
||||
vertical-align: middle;
|
||||
padding-right: 4px;
|
||||
}
|
||||
.manage-groups a.add.page-title-action img.icon,
|
||||
.manage-groups a.refresh.page-title-action img.icon,
|
||||
.manage-groups .row-actions span.edit img,
|
||||
.manage-groups .row-actions span.remove img,
|
||||
.manage-capabilities a.add.page-title-action img.icon,
|
||||
.manage-capabilities a.refresh.page-title-action img.icon,
|
||||
.manage-capabilities .row-actions span.edit img,
|
||||
.manage-capabilities .row-actions span.remove img {
|
||||
vertical-align: sub;
|
||||
}
|
||||
.manage a.add .label {
|
||||
height: 20px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.manage a.refresh {
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
float: right;
|
||||
}
|
||||
.manage a.refresh .icon {
|
||||
vertical-align: middle;
|
||||
}
|
||||
.manage a.refresh .label {
|
||||
height: 20px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
div.field {
|
||||
padding-bottom: 0.62em;
|
||||
}
|
||||
div.field a.cancel {
|
||||
vertical-align: middle;
|
||||
}
|
||||
div.field input[type="submit"],
|
||||
div.group.remove input[type="submit"],
|
||||
div.capability.remove input[type="submit"] {
|
||||
margin-right: 1em;
|
||||
}
|
||||
label.group-id-filter,
|
||||
label.group-name-filter,
|
||||
label.capability-id-filter,
|
||||
label.capability-filter,
|
||||
label.field-label {
|
||||
padding-right: 0.62em;
|
||||
}
|
||||
input.group-id-filter,
|
||||
input.group-name-filter,
|
||||
input.capability-id-filter,
|
||||
input.capability-filter {
|
||||
margin-right: 0.31em;
|
||||
}
|
||||
|
||||
div.group.new label,
|
||||
div.group.edit label,
|
||||
div.capability.new label,
|
||||
div.capability.edit label {
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
.groups-overview .page-options,
|
||||
.capabilities-overview .page-options {
|
||||
float: left;
|
||||
display: inline;
|
||||
/*width: 250px;*/
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
.groups-overview .page-options.right,
|
||||
.capabilities-overview .page-options.right {
|
||||
float: right;
|
||||
}
|
||||
.groups-overview .page-options label,
|
||||
.groups-overview .page-options input[type="text"],
|
||||
.groups-overview .page-options .button,
|
||||
.capabilities-overview .page-options label,
|
||||
.capabilities-overview .page-options input[type="text"],
|
||||
.capabilities-overview .page-options .button {
|
||||
margin-right: 4px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.groups-overview .page-options input[type="text"],
|
||||
.capabilities-overview .page-options input[type="text"] {
|
||||
width: 2.8em;
|
||||
text-align: center;
|
||||
}
|
||||
.groups-overview .tablenav.top,
|
||||
.capabilities-overview .tablenav.top {
|
||||
display: inline;
|
||||
width: 250px;
|
||||
}
|
||||
.groups-overview th.check-column,
|
||||
.capabilities-overview th.check-column {
|
||||
padding: 10px 0 0 3px;
|
||||
}
|
||||
.groups-overview th.edit,
|
||||
.groups-overview th.group_id,
|
||||
.groups-overview th.remove,
|
||||
.capabilities-overview th.capability_id,
|
||||
.capabilities-overview th.edit,
|
||||
.capabilities-overview th.remove {
|
||||
width:10%;
|
||||
}
|
||||
|
||||
.groups-overview td.capabilities,
|
||||
.capabilities-overview td.capability {
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.groups-overview td.capabilities ul {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.groups-overview td.capabilities span.inherited {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.manage-groups .required,
|
||||
.manage-capabilities .required {
|
||||
background: url(../images/required.png) transparent no-repeat left center;
|
||||
padding-left: 16px ! important;
|
||||
}
|
||||
.groups-footer {
|
||||
padding-top: 16px;
|
||||
color: #999;
|
||||
}
|
||||
.groups-footer a {
|
||||
color: #777;
|
||||
text-decoration: none;
|
||||
font-weight: bold;
|
||||
}
|
||||
.groups-footer form {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.groups-options input[type="checkbox"] {
|
||||
margin-right: 4px;
|
||||
}
|
||||
.groups-options .groups-permissions thead td {
|
||||
padding: 0 4px;
|
||||
font-weight: bold;
|
||||
margin: 0;
|
||||
}
|
||||
.groups-options .groups-permissions td.checkbox {
|
||||
text-align: center;
|
||||
}
|
||||
.info {
|
||||
background-color: #eec;
|
||||
border: 1px solid #cca;
|
||||
border-radius: 4px;
|
||||
margin: 1em;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
#groups-extensions-box {
|
||||
position: relative;
|
||||
float: right;
|
||||
padding: 1em;
|
||||
margin: 1em;
|
||||
background-color: #eee;
|
||||
border-radius: 4px;
|
||||
color: #333;
|
||||
border: 1px solid #ddd;
|
||||
width: 256px;
|
||||
}
|
||||
#groups-extensions-box a.close {
|
||||
color: #ccc;
|
||||
line-height: 18px;
|
||||
position: absolute;
|
||||
right: 6px;
|
||||
text-decoration: none;
|
||||
top: 6px;
|
||||
font-family: sans;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.groups-bulk-container {
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
.groups-bulk-container,
|
||||
.capabilities-bulk-container,
|
||||
.tablenav .tablenav-pages {
|
||||
line-height: 24px;
|
||||
}
|
||||
.groups-bulk-container .capabilities-select-container {
|
||||
width: 25%;
|
||||
float: left;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.groups-bulk-container .selectize-control,
|
||||
.groups-bulk-container select.bulk-action,
|
||||
.capabilities-bulk-container select.bulk-action {
|
||||
margin-right: 4px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.groups-bulk-container .selectize-input {
|
||||
font-size: inherit;
|
||||
line-height: 18px;
|
||||
padding: 0 4px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.groups-bulk-container .selectize-input input[type="text"] {
|
||||
font-size: inherit;
|
||||
vertical-align: middle;
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
}
|
||||
.groups-bulk-container .button,
|
||||
.capabilities-bulk-container .button {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.group.new .selectize-control,
|
||||
.group.edit .selectize-control,
|
||||
.groups-options .selectize-control {
|
||||
padding: 2px;
|
||||
}
|
||||
.group.new .selectize-input,
|
||||
.group.edit .selectize-input,
|
||||
.groups-options .selectize-input {
|
||||
font-size: inherit;
|
||||
line-height: 18px;
|
||||
padding: 2px;
|
||||
}
|
||||
|
||||
.groups.messages {
|
||||
padding: 1em 0;
|
||||
}
|
||||
|
||||
@media screen and ( max-width: 782px ) {
|
||||
.groups-bulk-container .selectize-input {
|
||||
padding: 2px;
|
||||
}
|
||||
.groups-bulk-container .selectize-input .item,
|
||||
.groups-bulk-container .selectize-dropdown-content .option {
|
||||
font-size: 16px;
|
||||
}
|
||||
.groups-bulk-container .selectize-input input[type="text"] {
|
||||
height: 32px;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.groups-admin-add-ons {
|
||||
margin: 10px 20px 0 2px;
|
||||
}
|
||||
.groups-admin-add-ons ul {
|
||||
list-style: none;
|
||||
display: block;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
.groups-admin-add-ons li {
|
||||
float: left;
|
||||
margin: 0 1em 1em 0;
|
||||
padding: 8px;
|
||||
vertical-align: top;
|
||||
width: 256px;
|
||||
height: 158px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
.groups-admin-add-ons .add-ons h3 {
|
||||
margin: 0;
|
||||
display: block;
|
||||
clear: both;
|
||||
}
|
||||
.groups-admin-add-ons .add-ons h3 img {
|
||||
vertical-align: middle;
|
||||
margin: 0 8px 0 0;
|
||||
display: block;
|
||||
float: left;
|
||||
}
|
||||
.groups-admin-add-ons .add-ons a {
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
}
|
||||
.groups-admin-add-ons .add-ons p {
|
||||
margin: 0;
|
||||
padding-top: 8px;
|
||||
clear: both;
|
||||
display: block;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.groups-welcome-panel {
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
margin: 1em;
|
||||
padding: 1em;
|
||||
border: 1px solid #e5e5e5;
|
||||
-webkit-box-shadow: 0 1px 1px rgba(0,0,0,0.04);
|
||||
box-shadow: 0 1px 1px rgba(0,0,0,0.04);
|
||||
background: #fff;
|
||||
font-size: 13px;
|
||||
line-height: 2.1em;
|
||||
}
|
||||
.groups-welcome-panel h2 {
|
||||
margin: 1.62em 0 0 0;
|
||||
font-size: 21px;
|
||||
font-weight: 400;
|
||||
line-height: 1.2;
|
||||
}
|
||||
.groups-welcome-panel h3 {
|
||||
margin: 1.33em 0 0;
|
||||
font-size: 16px;
|
||||
}
|
||||
.groups-welcome-panel li {
|
||||
font-size: 14px;
|
||||
}
|
||||
.groups-welcome-panel p {
|
||||
color: #72777c;
|
||||
}
|
||||
.groups-welcome-panel a {
|
||||
text-decoration: none;
|
||||
}
|
||||
.groups-welcome-panel .headline {
|
||||
font-size: 17px;
|
||||
margin: 1em 0 1.62em 0;
|
||||
}
|
||||
|
||||
.groups-welcome-panel-content {
|
||||
margin-left: 13px;
|
||||
max-width: 1500px;
|
||||
}
|
||||
.groups-welcome-icon {
|
||||
float: right;
|
||||
margin: 0 0.62em 0.62em 0.62em;
|
||||
}
|
||||
.groups-welcome-panel .important {
|
||||
border: 1px solid #e5e5e5;
|
||||
border-left: 2px solid #ffcc00;
|
||||
padding: 1em;
|
||||
-webkit-box-shadow: 0 1px 1px rgba(0,0,0,0.04);
|
||||
box-shadow: 0 1px 1px rgba(0,0,0,0.04);
|
||||
}
|
||||
.groups-welcome-panel .indent {
|
||||
border: 1px solid #e5e5e5;
|
||||
padding: 0 0.62em;
|
||||
}
|
||||
@@ -0,0 +1,487 @@
|
||||
/**
|
||||
* selectize.bootstrap2.css (v0.12.1) - Bootstrap 2 Theme
|
||||
* Copyright (c) 2013–2015 Brian Reavis & contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
|
||||
* file except in compliance with the License. You may obtain a copy of the License at:
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under
|
||||
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
|
||||
* ANY KIND, either express or implied. See the License for the specific language
|
||||
* governing permissions and limitations under the License.
|
||||
*
|
||||
* @author Brian Reavis <brian@thirdroute.com>
|
||||
*/
|
||||
.selectize-control.plugin-drag_drop.multi > .selectize-input > div.ui-sortable-placeholder {
|
||||
visibility: visible !important;
|
||||
background: #f2f2f2 !important;
|
||||
background: rgba(0, 0, 0, 0.06) !important;
|
||||
border: 0 none !important;
|
||||
-webkit-box-shadow: inset 0 0 12px 4px #ffffff;
|
||||
box-shadow: inset 0 0 12px 4px #ffffff;
|
||||
}
|
||||
.selectize-control.plugin-drag_drop .ui-sortable-placeholder::after {
|
||||
content: '!';
|
||||
visibility: hidden;
|
||||
}
|
||||
.selectize-control.plugin-drag_drop .ui-sortable-helper {
|
||||
-webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
.selectize-dropdown-header {
|
||||
position: relative;
|
||||
padding: 3px 10px;
|
||||
border-bottom: 1px solid #d0d0d0;
|
||||
background: #f8f8f8;
|
||||
-webkit-border-radius: 4px 4px 0 0;
|
||||
-moz-border-radius: 4px 4px 0 0;
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
.selectize-dropdown-header-close {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 50%;
|
||||
color: #333333;
|
||||
opacity: 0.4;
|
||||
margin-top: -12px;
|
||||
line-height: 20px;
|
||||
font-size: 20px !important;
|
||||
}
|
||||
.selectize-dropdown-header-close:hover {
|
||||
color: #000000;
|
||||
}
|
||||
.selectize-dropdown.plugin-optgroup_columns .optgroup {
|
||||
border-right: 1px solid #f2f2f2;
|
||||
border-top: 0 none;
|
||||
float: left;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.selectize-dropdown.plugin-optgroup_columns .optgroup:last-child {
|
||||
border-right: 0 none;
|
||||
}
|
||||
.selectize-dropdown.plugin-optgroup_columns .optgroup:before {
|
||||
display: none;
|
||||
}
|
||||
.selectize-dropdown.plugin-optgroup_columns .optgroup-header {
|
||||
border-top: 0 none;
|
||||
}
|
||||
.selectize-control.plugin-remove_button [data-value] {
|
||||
position: relative;
|
||||
padding-right: 24px !important;
|
||||
}
|
||||
.selectize-control.plugin-remove_button [data-value] .remove {
|
||||
z-index: 1;
|
||||
/* fixes ie bug (see #392) */
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 17px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
padding: 1px 0 0 0;
|
||||
border-left: 1px solid #cccccc;
|
||||
-webkit-border-radius: 0 2px 2px 0;
|
||||
-moz-border-radius: 0 2px 2px 0;
|
||||
border-radius: 0 2px 2px 0;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.selectize-control.plugin-remove_button [data-value] .remove:hover {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.selectize-control.plugin-remove_button [data-value].active .remove {
|
||||
border-left-color: #0077b3;
|
||||
}
|
||||
.selectize-control.plugin-remove_button .disabled [data-value] .remove:hover {
|
||||
background: none;
|
||||
}
|
||||
.selectize-control.plugin-remove_button .disabled [data-value] .remove {
|
||||
border-left-color: #e0e0e0;
|
||||
}
|
||||
.selectize-control {
|
||||
position: relative;
|
||||
}
|
||||
.selectize-dropdown,
|
||||
.selectize-input,
|
||||
.selectize-input input {
|
||||
color: #333333;
|
||||
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
-webkit-font-smoothing: inherit;
|
||||
}
|
||||
.selectize-input,
|
||||
.selectize-control.single .selectize-input.input-active {
|
||||
background: #ffffff;
|
||||
cursor: text;
|
||||
display: inline-block;
|
||||
}
|
||||
.selectize-input {
|
||||
border: 1px solid #d0d0d0;
|
||||
padding: 7px 10px;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.selectize-control.multi .selectize-input.has-items {
|
||||
padding: 5px 10px 2px;
|
||||
}
|
||||
.selectize-input.full {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
.selectize-input.disabled,
|
||||
.selectize-input.disabled * {
|
||||
cursor: default !important;
|
||||
}
|
||||
.selectize-input.focus {
|
||||
-webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15);
|
||||
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
.selectize-input.dropdown-active {
|
||||
-webkit-border-radius: 4px 4px 0 0;
|
||||
-moz-border-radius: 4px 4px 0 0;
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
.selectize-input > * {
|
||||
vertical-align: baseline;
|
||||
display: -moz-inline-stack;
|
||||
display: inline-block;
|
||||
zoom: 1;
|
||||
*display: inline;
|
||||
}
|
||||
.selectize-control.multi .selectize-input > div {
|
||||
cursor: pointer;
|
||||
margin: 0 3px 3px 0;
|
||||
padding: 1px 3px;
|
||||
background: #e6e6e6;
|
||||
color: #333333;
|
||||
border: 1px solid #cccccc;
|
||||
}
|
||||
.selectize-control.multi .selectize-input > div.active {
|
||||
background: #0088cc;
|
||||
color: #ffffff;
|
||||
border: 1px solid #0077b3;
|
||||
}
|
||||
.selectize-control.multi .selectize-input.disabled > div,
|
||||
.selectize-control.multi .selectize-input.disabled > div.active {
|
||||
color: #474747;
|
||||
background: #fafafa;
|
||||
border: 1px solid #e0e0e0;
|
||||
}
|
||||
.selectize-input > input {
|
||||
display: inline-block !important;
|
||||
padding: 0 !important;
|
||||
min-height: 0 !important;
|
||||
max-height: none !important;
|
||||
max-width: 100% !important;
|
||||
margin: 0 !important;
|
||||
text-indent: 0 !important;
|
||||
border: 0 none !important;
|
||||
background: none !important;
|
||||
line-height: inherit !important;
|
||||
-webkit-user-select: auto !important;
|
||||
-webkit-box-shadow: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
.selectize-input > input::-ms-clear {
|
||||
display: none;
|
||||
}
|
||||
.selectize-input > input:focus {
|
||||
outline: none !important;
|
||||
}
|
||||
.selectize-input::after {
|
||||
content: ' ';
|
||||
display: block;
|
||||
clear: left;
|
||||
}
|
||||
.selectize-input.dropdown-active::before {
|
||||
content: ' ';
|
||||
display: block;
|
||||
position: absolute;
|
||||
background: #e5e5e5;
|
||||
height: 1px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
.selectize-dropdown {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
border: 1px solid #cccccc;
|
||||
background: #ffffff;
|
||||
margin: -1px 0 0 0;
|
||||
border-top: 0 none;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
-webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
-webkit-border-radius: 0 0 4px 4px;
|
||||
-moz-border-radius: 0 0 4px 4px;
|
||||
border-radius: 0 0 4px 4px;
|
||||
}
|
||||
.selectize-dropdown [data-selectable] {
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
}
|
||||
.selectize-dropdown [data-selectable] .highlight {
|
||||
background: rgba(255, 237, 40, 0.4);
|
||||
-webkit-border-radius: 1px;
|
||||
-moz-border-radius: 1px;
|
||||
border-radius: 1px;
|
||||
}
|
||||
.selectize-dropdown [data-selectable],
|
||||
.selectize-dropdown .optgroup-header {
|
||||
padding: 3px 10px;
|
||||
}
|
||||
.selectize-dropdown .optgroup:first-child .optgroup-header {
|
||||
border-top: 0 none;
|
||||
}
|
||||
.selectize-dropdown .optgroup-header {
|
||||
color: #999999;
|
||||
background: #ffffff;
|
||||
cursor: default;
|
||||
}
|
||||
.selectize-dropdown .active {
|
||||
background-color: #0088cc;
|
||||
color: #ffffff;
|
||||
}
|
||||
.selectize-dropdown .active.create {
|
||||
color: #ffffff;
|
||||
}
|
||||
.selectize-dropdown .create {
|
||||
color: rgba(51, 51, 51, 0.5);
|
||||
}
|
||||
.selectize-dropdown-content {
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
max-height: 200px;
|
||||
}
|
||||
.selectize-control.single .selectize-input,
|
||||
.selectize-control.single .selectize-input input {
|
||||
cursor: pointer;
|
||||
}
|
||||
.selectize-control.single .selectize-input.input-active,
|
||||
.selectize-control.single .selectize-input.input-active input {
|
||||
cursor: text;
|
||||
}
|
||||
.selectize-control.single .selectize-input:after {
|
||||
content: ' ';
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 15px;
|
||||
margin-top: -3px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-style: solid;
|
||||
border-width: 5px 5px 0 5px;
|
||||
border-color: #000000 transparent transparent transparent;
|
||||
}
|
||||
.selectize-control.single .selectize-input.dropdown-active:after {
|
||||
margin-top: -4px;
|
||||
border-width: 0 5px 5px 5px;
|
||||
border-color: transparent transparent #000000 transparent;
|
||||
}
|
||||
.selectize-control.rtl.single .selectize-input:after {
|
||||
left: 15px;
|
||||
right: auto;
|
||||
}
|
||||
.selectize-control.rtl .selectize-input > input {
|
||||
margin: 0 4px 0 -2px !important;
|
||||
}
|
||||
.selectize-control .selectize-input.disabled {
|
||||
opacity: 0.5;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
.selectize-dropdown {
|
||||
margin: 2px 0 0 0;
|
||||
z-index: 1000;
|
||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||
border-radius: 4px;
|
||||
-webkit-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
|
||||
-moz-box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
|
||||
box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
.selectize-dropdown .optgroup-header {
|
||||
font-size: 11px;
|
||||
font-weight: bold;
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5);
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.selectize-dropdown .optgroup:first-child:before {
|
||||
display: none;
|
||||
}
|
||||
.selectize-dropdown .optgroup:before {
|
||||
content: ' ';
|
||||
display: block;
|
||||
*width: 100%;
|
||||
height: 1px;
|
||||
margin: 9px 1px;
|
||||
*margin: -5px 0 5px;
|
||||
overflow: hidden;
|
||||
background-color: #e5e5e5;
|
||||
border-bottom: 1px solid #ffffff;
|
||||
margin-left: -10px;
|
||||
margin-right: -10px;
|
||||
}
|
||||
.selectize-dropdown [data-selectable].active {
|
||||
background-color: #0081c2;
|
||||
background-image: -moz-linear-gradient(top, #0088cc, #0077b3);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3));
|
||||
background-image: -webkit-linear-gradient(top, #0088cc, #0077b3);
|
||||
background-image: -o-linear-gradient(top, #0088cc, #0077b3);
|
||||
background-image: linear-gradient(to bottom, #0088cc, #0077b3);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0);
|
||||
}
|
||||
.selectize-dropdown-content {
|
||||
padding: 5px 0;
|
||||
}
|
||||
.selectize-dropdown-header {
|
||||
padding: 6px 10px;
|
||||
}
|
||||
.selectize-input {
|
||||
-webkit-transition: border linear .2s, box-shadow linear .2s;
|
||||
-moz-transition: border linear .2s, box-shadow linear .2s;
|
||||
-o-transition: border linear .2s, box-shadow linear .2s;
|
||||
transition: border linear .2s, box-shadow linear .2s;
|
||||
}
|
||||
.selectize-input.dropdown-active {
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.selectize-input.dropdown-active::before {
|
||||
display: none;
|
||||
}
|
||||
.selectize-input.input-active,
|
||||
.selectize-input.input-active:hover,
|
||||
.selectize-control.multi .selectize-input.focus {
|
||||
background: #ffffff !important;
|
||||
border-color: rgba(82, 168, 236, 0.8) !important;
|
||||
outline: 0 !important;
|
||||
outline: thin dotted \9 !important;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6) !important;
|
||||
-moz-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6) !important;
|
||||
box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(82,168,236,.6) !important;
|
||||
}
|
||||
.selectize-control.single .selectize-input {
|
||||
color: #333333;
|
||||
text-shadow: 0 1px 1px rgba(255, 255, 255, 0.75);
|
||||
background-color: #f5f5f5;
|
||||
background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6));
|
||||
background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6);
|
||||
background-image: -o-linear-gradient(top, #ffffff, #e6e6e6);
|
||||
background-image: linear-gradient(to bottom, #ffffff, #e6e6e6);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0);
|
||||
border-color: #e6e6e6 #e6e6e6 #bfbfbf;
|
||||
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
|
||||
*background-color: #e6e6e6;
|
||||
/* Darken IE7 buttons by default so they stand out more given they won't have borders */
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
|
||||
-webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
|
||||
-moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
|
||||
box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
|
||||
}
|
||||
.selectize-control.single .selectize-input:hover,
|
||||
.selectize-control.single .selectize-input:focus,
|
||||
.selectize-control.single .selectize-input:active,
|
||||
.selectize-control.single .selectize-input.active,
|
||||
.selectize-control.single .selectize-input.disabled,
|
||||
.selectize-control.single .selectize-input[disabled] {
|
||||
color: #333333;
|
||||
background-color: #e6e6e6;
|
||||
*background-color: #d9d9d9;
|
||||
}
|
||||
.selectize-control.single .selectize-input:active,
|
||||
.selectize-control.single .selectize-input.active {
|
||||
background-color: #cccccc \9;
|
||||
}
|
||||
.selectize-control.single .selectize-input:hover {
|
||||
color: #333333;
|
||||
text-decoration: none;
|
||||
background-position: 0 -15px;
|
||||
-webkit-transition: background-position 0.1s linear;
|
||||
-moz-transition: background-position 0.1s linear;
|
||||
-o-transition: background-position 0.1s linear;
|
||||
transition: background-position 0.1s linear;
|
||||
}
|
||||
.selectize-control.single .selectize-input.disabled {
|
||||
background: #e6e6e6 !important;
|
||||
-webkit-box-shadow: none;
|
||||
-moz-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
.selectize-control.multi .selectize-input {
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
}
|
||||
.selectize-control.multi .selectize-input.has-items {
|
||||
padding-left: 7px;
|
||||
padding-right: 7px;
|
||||
}
|
||||
.selectize-control.multi .selectize-input > div {
|
||||
color: #333333;
|
||||
text-shadow: none;
|
||||
background-color: #f5f5f5;
|
||||
background-image: -moz-linear-gradient(top, #ffffff, #e6e6e6);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#ffffff), to(#e6e6e6));
|
||||
background-image: -webkit-linear-gradient(top, #ffffff, #e6e6e6);
|
||||
background-image: -o-linear-gradient(top, #ffffff, #e6e6e6);
|
||||
background-image: linear-gradient(to bottom, #ffffff, #e6e6e6);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe6e6e6', GradientType=0);
|
||||
border-color: #e6e6e6 #e6e6e6 #bfbfbf;
|
||||
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
|
||||
*background-color: #e6e6e6;
|
||||
border: 1px solid #cccccc;
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
-webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
|
||||
-moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
|
||||
box-shadow: inset 0 1px 0 rgba(255,255,255,.2), 0 1px 2px rgba(0,0,0,.05);
|
||||
}
|
||||
.selectize-control.multi .selectize-input > div.active {
|
||||
-webkit-box-shadow: 0 1px 2px rgba(0,0,0,.05);
|
||||
-moz-box-shadow: 0 1px 2px rgba(0,0,0,.05);
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,.05);
|
||||
color: #ffffff;
|
||||
text-shadow: none;
|
||||
background-color: #0081c2;
|
||||
background-image: -moz-linear-gradient(top, #0088cc, #0077b3);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0077b3));
|
||||
background-image: -webkit-linear-gradient(top, #0088cc, #0077b3);
|
||||
background-image: -o-linear-gradient(top, #0088cc, #0077b3);
|
||||
background-image: linear-gradient(to bottom, #0088cc, #0077b3);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff0088cc', endColorstr='#ff0077b3', GradientType=0);
|
||||
border-color: #0077b3 #0077b3 #004466;
|
||||
border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25);
|
||||
*background-color: #0088cc;
|
||||
border: 1px solid #0088cc;
|
||||
}
|
||||
@@ -0,0 +1,401 @@
|
||||
/**
|
||||
* selectize.bootstrap3.css (v0.12.1) - Bootstrap 3 Theme
|
||||
* Copyright (c) 2013–2015 Brian Reavis & contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
|
||||
* file except in compliance with the License. You may obtain a copy of the License at:
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under
|
||||
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
|
||||
* ANY KIND, either express or implied. See the License for the specific language
|
||||
* governing permissions and limitations under the License.
|
||||
*
|
||||
* @author Brian Reavis <brian@thirdroute.com>
|
||||
*/
|
||||
.selectize-control.plugin-drag_drop.multi > .selectize-input > div.ui-sortable-placeholder {
|
||||
visibility: visible !important;
|
||||
background: #f2f2f2 !important;
|
||||
background: rgba(0, 0, 0, 0.06) !important;
|
||||
border: 0 none !important;
|
||||
-webkit-box-shadow: inset 0 0 12px 4px #ffffff;
|
||||
box-shadow: inset 0 0 12px 4px #ffffff;
|
||||
}
|
||||
.selectize-control.plugin-drag_drop .ui-sortable-placeholder::after {
|
||||
content: '!';
|
||||
visibility: hidden;
|
||||
}
|
||||
.selectize-control.plugin-drag_drop .ui-sortable-helper {
|
||||
-webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
.selectize-dropdown-header {
|
||||
position: relative;
|
||||
padding: 3px 12px;
|
||||
border-bottom: 1px solid #d0d0d0;
|
||||
background: #f8f8f8;
|
||||
-webkit-border-radius: 4px 4px 0 0;
|
||||
-moz-border-radius: 4px 4px 0 0;
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
.selectize-dropdown-header-close {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
top: 50%;
|
||||
color: #333333;
|
||||
opacity: 0.4;
|
||||
margin-top: -12px;
|
||||
line-height: 20px;
|
||||
font-size: 20px !important;
|
||||
}
|
||||
.selectize-dropdown-header-close:hover {
|
||||
color: #000000;
|
||||
}
|
||||
.selectize-dropdown.plugin-optgroup_columns .optgroup {
|
||||
border-right: 1px solid #f2f2f2;
|
||||
border-top: 0 none;
|
||||
float: left;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.selectize-dropdown.plugin-optgroup_columns .optgroup:last-child {
|
||||
border-right: 0 none;
|
||||
}
|
||||
.selectize-dropdown.plugin-optgroup_columns .optgroup:before {
|
||||
display: none;
|
||||
}
|
||||
.selectize-dropdown.plugin-optgroup_columns .optgroup-header {
|
||||
border-top: 0 none;
|
||||
}
|
||||
.selectize-control.plugin-remove_button [data-value] {
|
||||
position: relative;
|
||||
padding-right: 24px !important;
|
||||
}
|
||||
.selectize-control.plugin-remove_button [data-value] .remove {
|
||||
z-index: 1;
|
||||
/* fixes ie bug (see #392) */
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 17px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
padding: 1px 0 0 0;
|
||||
border-left: 1px solid rgba(0, 0, 0, 0);
|
||||
-webkit-border-radius: 0 2px 2px 0;
|
||||
-moz-border-radius: 0 2px 2px 0;
|
||||
border-radius: 0 2px 2px 0;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.selectize-control.plugin-remove_button [data-value] .remove:hover {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.selectize-control.plugin-remove_button [data-value].active .remove {
|
||||
border-left-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
.selectize-control.plugin-remove_button .disabled [data-value] .remove:hover {
|
||||
background: none;
|
||||
}
|
||||
.selectize-control.plugin-remove_button .disabled [data-value] .remove {
|
||||
border-left-color: rgba(77, 77, 77, 0);
|
||||
}
|
||||
.selectize-control {
|
||||
position: relative;
|
||||
}
|
||||
.selectize-dropdown,
|
||||
.selectize-input,
|
||||
.selectize-input input {
|
||||
color: #333333;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: 20px;
|
||||
-webkit-font-smoothing: inherit;
|
||||
}
|
||||
.selectize-input,
|
||||
.selectize-control.single .selectize-input.input-active {
|
||||
background: #ffffff;
|
||||
cursor: text;
|
||||
display: inline-block;
|
||||
}
|
||||
.selectize-input {
|
||||
border: 1px solid #cccccc;
|
||||
padding: 6px 12px;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.selectize-control.multi .selectize-input.has-items {
|
||||
padding: 5px 12px 2px;
|
||||
}
|
||||
.selectize-input.full {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
.selectize-input.disabled,
|
||||
.selectize-input.disabled * {
|
||||
cursor: default !important;
|
||||
}
|
||||
.selectize-input.focus {
|
||||
-webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15);
|
||||
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
.selectize-input.dropdown-active {
|
||||
-webkit-border-radius: 4px 4px 0 0;
|
||||
-moz-border-radius: 4px 4px 0 0;
|
||||
border-radius: 4px 4px 0 0;
|
||||
}
|
||||
.selectize-input > * {
|
||||
vertical-align: baseline;
|
||||
display: -moz-inline-stack;
|
||||
display: inline-block;
|
||||
zoom: 1;
|
||||
*display: inline;
|
||||
}
|
||||
.selectize-control.multi .selectize-input > div {
|
||||
cursor: pointer;
|
||||
margin: 0 3px 3px 0;
|
||||
padding: 1px 3px;
|
||||
background: #efefef;
|
||||
color: #333333;
|
||||
border: 0 solid rgba(0, 0, 0, 0);
|
||||
}
|
||||
.selectize-control.multi .selectize-input > div.active {
|
||||
background: #428bca;
|
||||
color: #ffffff;
|
||||
border: 0 solid rgba(0, 0, 0, 0);
|
||||
}
|
||||
.selectize-control.multi .selectize-input.disabled > div,
|
||||
.selectize-control.multi .selectize-input.disabled > div.active {
|
||||
color: #808080;
|
||||
background: #ffffff;
|
||||
border: 0 solid rgba(77, 77, 77, 0);
|
||||
}
|
||||
.selectize-input > input {
|
||||
display: inline-block !important;
|
||||
padding: 0 !important;
|
||||
min-height: 0 !important;
|
||||
max-height: none !important;
|
||||
max-width: 100% !important;
|
||||
margin: 0 !important;
|
||||
text-indent: 0 !important;
|
||||
border: 0 none !important;
|
||||
background: none !important;
|
||||
line-height: inherit !important;
|
||||
-webkit-user-select: auto !important;
|
||||
-webkit-box-shadow: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
.selectize-input > input::-ms-clear {
|
||||
display: none;
|
||||
}
|
||||
.selectize-input > input:focus {
|
||||
outline: none !important;
|
||||
}
|
||||
.selectize-input::after {
|
||||
content: ' ';
|
||||
display: block;
|
||||
clear: left;
|
||||
}
|
||||
.selectize-input.dropdown-active::before {
|
||||
content: ' ';
|
||||
display: block;
|
||||
position: absolute;
|
||||
background: #ffffff;
|
||||
height: 1px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
.selectize-dropdown {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
border: 1px solid #d0d0d0;
|
||||
background: #ffffff;
|
||||
margin: -1px 0 0 0;
|
||||
border-top: 0 none;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
-webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
-webkit-border-radius: 0 0 4px 4px;
|
||||
-moz-border-radius: 0 0 4px 4px;
|
||||
border-radius: 0 0 4px 4px;
|
||||
}
|
||||
.selectize-dropdown [data-selectable] {
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
}
|
||||
.selectize-dropdown [data-selectable] .highlight {
|
||||
background: rgba(255, 237, 40, 0.4);
|
||||
-webkit-border-radius: 1px;
|
||||
-moz-border-radius: 1px;
|
||||
border-radius: 1px;
|
||||
}
|
||||
.selectize-dropdown [data-selectable],
|
||||
.selectize-dropdown .optgroup-header {
|
||||
padding: 3px 12px;
|
||||
}
|
||||
.selectize-dropdown .optgroup:first-child .optgroup-header {
|
||||
border-top: 0 none;
|
||||
}
|
||||
.selectize-dropdown .optgroup-header {
|
||||
color: #777777;
|
||||
background: #ffffff;
|
||||
cursor: default;
|
||||
}
|
||||
.selectize-dropdown .active {
|
||||
background-color: #f5f5f5;
|
||||
color: #262626;
|
||||
}
|
||||
.selectize-dropdown .active.create {
|
||||
color: #262626;
|
||||
}
|
||||
.selectize-dropdown .create {
|
||||
color: rgba(51, 51, 51, 0.5);
|
||||
}
|
||||
.selectize-dropdown-content {
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
max-height: 200px;
|
||||
}
|
||||
.selectize-control.single .selectize-input,
|
||||
.selectize-control.single .selectize-input input {
|
||||
cursor: pointer;
|
||||
}
|
||||
.selectize-control.single .selectize-input.input-active,
|
||||
.selectize-control.single .selectize-input.input-active input {
|
||||
cursor: text;
|
||||
}
|
||||
.selectize-control.single .selectize-input:after {
|
||||
content: ' ';
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 17px;
|
||||
margin-top: -3px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-style: solid;
|
||||
border-width: 5px 5px 0 5px;
|
||||
border-color: #333333 transparent transparent transparent;
|
||||
}
|
||||
.selectize-control.single .selectize-input.dropdown-active:after {
|
||||
margin-top: -4px;
|
||||
border-width: 0 5px 5px 5px;
|
||||
border-color: transparent transparent #333333 transparent;
|
||||
}
|
||||
.selectize-control.rtl.single .selectize-input:after {
|
||||
left: 17px;
|
||||
right: auto;
|
||||
}
|
||||
.selectize-control.rtl .selectize-input > input {
|
||||
margin: 0 4px 0 -2px !important;
|
||||
}
|
||||
.selectize-control .selectize-input.disabled {
|
||||
opacity: 0.5;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
.selectize-dropdown,
|
||||
.selectize-dropdown.form-control {
|
||||
height: auto;
|
||||
padding: 0;
|
||||
margin: 2px 0 0 0;
|
||||
z-index: 1000;
|
||||
background: #ffffff;
|
||||
border: 1px solid #cccccc;
|
||||
border: 1px solid rgba(0, 0, 0, 0.15);
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
-webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
|
||||
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
|
||||
}
|
||||
.selectize-dropdown .optgroup-header {
|
||||
font-size: 12px;
|
||||
line-height: 1.42857143;
|
||||
}
|
||||
.selectize-dropdown .optgroup:first-child:before {
|
||||
display: none;
|
||||
}
|
||||
.selectize-dropdown .optgroup:before {
|
||||
content: ' ';
|
||||
display: block;
|
||||
height: 1px;
|
||||
margin: 9px 0;
|
||||
overflow: hidden;
|
||||
background-color: #e5e5e5;
|
||||
margin-left: -12px;
|
||||
margin-right: -12px;
|
||||
}
|
||||
.selectize-dropdown-content {
|
||||
padding: 5px 0;
|
||||
}
|
||||
.selectize-dropdown-header {
|
||||
padding: 6px 12px;
|
||||
}
|
||||
.selectize-input {
|
||||
min-height: 34px;
|
||||
}
|
||||
.selectize-input.dropdown-active {
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.selectize-input.dropdown-active::before {
|
||||
display: none;
|
||||
}
|
||||
.selectize-input.focus {
|
||||
border-color: #66afe9;
|
||||
outline: 0;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);
|
||||
box-shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px rgba(102, 175, 233, 0.6);
|
||||
}
|
||||
.has-error .selectize-input {
|
||||
border-color: #a94442;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);
|
||||
}
|
||||
.has-error .selectize-input:focus {
|
||||
border-color: #843534;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 6px #ce8483;
|
||||
}
|
||||
.selectize-control.multi .selectize-input.has-items {
|
||||
padding-left: 9px;
|
||||
padding-right: 9px;
|
||||
}
|
||||
.selectize-control.multi .selectize-input > div {
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.form-control.selectize-control {
|
||||
padding: 0;
|
||||
height: auto;
|
||||
border: none;
|
||||
background: none;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
-webkit-border-radius: 0;
|
||||
-moz-border-radius: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
@@ -0,0 +1,317 @@
|
||||
/**
|
||||
* selectize.css (v0.12.1)
|
||||
* Copyright (c) 2013–2015 Brian Reavis & contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
|
||||
* file except in compliance with the License. You may obtain a copy of the License at:
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under
|
||||
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
|
||||
* ANY KIND, either express or implied. See the License for the specific language
|
||||
* governing permissions and limitations under the License.
|
||||
*
|
||||
* @author Brian Reavis <brian@thirdroute.com>
|
||||
*/
|
||||
|
||||
.selectize-control.plugin-drag_drop.multi > .selectize-input > div.ui-sortable-placeholder {
|
||||
visibility: visible !important;
|
||||
background: #f2f2f2 !important;
|
||||
background: rgba(0, 0, 0, 0.06) !important;
|
||||
border: 0 none !important;
|
||||
-webkit-box-shadow: inset 0 0 12px 4px #ffffff;
|
||||
box-shadow: inset 0 0 12px 4px #ffffff;
|
||||
}
|
||||
.selectize-control.plugin-drag_drop .ui-sortable-placeholder::after {
|
||||
content: '!';
|
||||
visibility: hidden;
|
||||
}
|
||||
.selectize-control.plugin-drag_drop .ui-sortable-helper {
|
||||
-webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
.selectize-dropdown-header {
|
||||
position: relative;
|
||||
padding: 5px 8px;
|
||||
border-bottom: 1px solid #d0d0d0;
|
||||
background: #f8f8f8;
|
||||
-webkit-border-radius: 3px 3px 0 0;
|
||||
-moz-border-radius: 3px 3px 0 0;
|
||||
border-radius: 3px 3px 0 0;
|
||||
}
|
||||
.selectize-dropdown-header-close {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 50%;
|
||||
color: #303030;
|
||||
opacity: 0.4;
|
||||
margin-top: -12px;
|
||||
line-height: 20px;
|
||||
font-size: 20px !important;
|
||||
}
|
||||
.selectize-dropdown-header-close:hover {
|
||||
color: #000000;
|
||||
}
|
||||
.selectize-dropdown.plugin-optgroup_columns .optgroup {
|
||||
border-right: 1px solid #f2f2f2;
|
||||
border-top: 0 none;
|
||||
float: left;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.selectize-dropdown.plugin-optgroup_columns .optgroup:last-child {
|
||||
border-right: 0 none;
|
||||
}
|
||||
.selectize-dropdown.plugin-optgroup_columns .optgroup:before {
|
||||
display: none;
|
||||
}
|
||||
.selectize-dropdown.plugin-optgroup_columns .optgroup-header {
|
||||
border-top: 0 none;
|
||||
}
|
||||
.selectize-control.plugin-remove_button [data-value] {
|
||||
position: relative;
|
||||
padding-right: 24px !important;
|
||||
}
|
||||
.selectize-control.plugin-remove_button [data-value] .remove {
|
||||
z-index: 1;
|
||||
/* fixes ie bug (see #392) */
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 17px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
padding: 2px 0 0 0;
|
||||
border-left: 1px solid #d0d0d0;
|
||||
-webkit-border-radius: 0 2px 2px 0;
|
||||
-moz-border-radius: 0 2px 2px 0;
|
||||
border-radius: 0 2px 2px 0;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.selectize-control.plugin-remove_button [data-value] .remove:hover {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.selectize-control.plugin-remove_button [data-value].active .remove {
|
||||
border-left-color: #cacaca;
|
||||
}
|
||||
.selectize-control.plugin-remove_button .disabled [data-value] .remove:hover {
|
||||
background: none;
|
||||
}
|
||||
.selectize-control.plugin-remove_button .disabled [data-value] .remove {
|
||||
border-left-color: #ffffff;
|
||||
}
|
||||
.selectize-control {
|
||||
position: relative;
|
||||
}
|
||||
.selectize-dropdown,
|
||||
.selectize-input,
|
||||
.selectize-input input {
|
||||
color: #303030;
|
||||
font-family: inherit;
|
||||
font-size: 13px;
|
||||
line-height: 18px;
|
||||
-webkit-font-smoothing: inherit;
|
||||
}
|
||||
.selectize-input,
|
||||
.selectize-control.single .selectize-input.input-active {
|
||||
background: #ffffff;
|
||||
cursor: text;
|
||||
display: inline-block;
|
||||
}
|
||||
.selectize-input {
|
||||
border: 1px solid #d0d0d0;
|
||||
padding: 8px 8px;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.1);
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.1);
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.selectize-control.multi .selectize-input.has-items {
|
||||
padding: 6px 8px 3px;
|
||||
}
|
||||
.selectize-input.full {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
.selectize-input.disabled,
|
||||
.selectize-input.disabled * {
|
||||
cursor: default !important;
|
||||
}
|
||||
.selectize-input.focus {
|
||||
-webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15);
|
||||
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
.selectize-input.dropdown-active {
|
||||
-webkit-border-radius: 3px 3px 0 0;
|
||||
-moz-border-radius: 3px 3px 0 0;
|
||||
border-radius: 3px 3px 0 0;
|
||||
}
|
||||
.selectize-input > * {
|
||||
vertical-align: baseline;
|
||||
display: -moz-inline-stack;
|
||||
display: inline-block;
|
||||
zoom: 1;
|
||||
*display: inline;
|
||||
}
|
||||
.selectize-control.multi .selectize-input > div {
|
||||
cursor: pointer;
|
||||
margin: 0 3px 3px 0;
|
||||
padding: 2px 6px;
|
||||
background: #f2f2f2;
|
||||
color: #303030;
|
||||
border: 0 solid #d0d0d0;
|
||||
}
|
||||
.selectize-control.multi .selectize-input > div.active {
|
||||
background: #e8e8e8;
|
||||
color: #303030;
|
||||
border: 0 solid #cacaca;
|
||||
}
|
||||
.selectize-control.multi .selectize-input.disabled > div,
|
||||
.selectize-control.multi .selectize-input.disabled > div.active {
|
||||
color: #7d7d7d;
|
||||
background: #ffffff;
|
||||
border: 0 solid #ffffff;
|
||||
}
|
||||
.selectize-input > input {
|
||||
display: inline-block !important;
|
||||
padding: 0 !important;
|
||||
min-height: 0 !important;
|
||||
max-height: none !important;
|
||||
max-width: 100% !important;
|
||||
margin: 0 2px 0 0 !important;
|
||||
text-indent: 0 !important;
|
||||
border: 0 none !important;
|
||||
background: none !important;
|
||||
line-height: inherit !important;
|
||||
-webkit-user-select: auto !important;
|
||||
-webkit-box-shadow: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
.selectize-input > input::-ms-clear {
|
||||
display: none;
|
||||
}
|
||||
.selectize-input > input:focus {
|
||||
outline: none !important;
|
||||
}
|
||||
.selectize-input::after {
|
||||
content: ' ';
|
||||
display: block;
|
||||
clear: left;
|
||||
}
|
||||
.selectize-input.dropdown-active::before {
|
||||
content: ' ';
|
||||
display: block;
|
||||
position: absolute;
|
||||
background: #f0f0f0;
|
||||
height: 1px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
.selectize-dropdown {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
border: 1px solid #d0d0d0;
|
||||
background: #ffffff;
|
||||
margin: -1px 0 0 0;
|
||||
border-top: 0 none;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
-webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
-webkit-border-radius: 0 0 3px 3px;
|
||||
-moz-border-radius: 0 0 3px 3px;
|
||||
border-radius: 0 0 3px 3px;
|
||||
}
|
||||
.selectize-dropdown [data-selectable] {
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
}
|
||||
.selectize-dropdown [data-selectable] .highlight {
|
||||
background: rgba(125, 168, 208, 0.2);
|
||||
-webkit-border-radius: 1px;
|
||||
-moz-border-radius: 1px;
|
||||
border-radius: 1px;
|
||||
}
|
||||
.selectize-dropdown [data-selectable],
|
||||
.selectize-dropdown .optgroup-header {
|
||||
padding: 5px 8px;
|
||||
}
|
||||
.selectize-dropdown .optgroup:first-child .optgroup-header {
|
||||
border-top: 0 none;
|
||||
}
|
||||
.selectize-dropdown .optgroup-header {
|
||||
color: #303030;
|
||||
background: #ffffff;
|
||||
cursor: default;
|
||||
}
|
||||
.selectize-dropdown .active {
|
||||
background-color: #f5fafd;
|
||||
color: #495c68;
|
||||
}
|
||||
.selectize-dropdown .active.create {
|
||||
color: #495c68;
|
||||
}
|
||||
.selectize-dropdown .create {
|
||||
color: rgba(48, 48, 48, 0.5);
|
||||
}
|
||||
.selectize-dropdown-content {
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
max-height: 200px;
|
||||
}
|
||||
.selectize-control.single .selectize-input,
|
||||
.selectize-control.single .selectize-input input {
|
||||
cursor: pointer;
|
||||
}
|
||||
.selectize-control.single .selectize-input.input-active,
|
||||
.selectize-control.single .selectize-input.input-active input {
|
||||
cursor: text;
|
||||
}
|
||||
.selectize-control.single .selectize-input:after {
|
||||
content: ' ';
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 15px;
|
||||
margin-top: -3px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-style: solid;
|
||||
border-width: 5px 5px 0 5px;
|
||||
border-color: #808080 transparent transparent transparent;
|
||||
}
|
||||
.selectize-control.single .selectize-input.dropdown-active:after {
|
||||
margin-top: -4px;
|
||||
border-width: 0 5px 5px 5px;
|
||||
border-color: transparent transparent #808080 transparent;
|
||||
}
|
||||
.selectize-control.rtl.single .selectize-input:after {
|
||||
left: 15px;
|
||||
right: auto;
|
||||
}
|
||||
.selectize-control.rtl .selectize-input > input {
|
||||
margin: 0 4px 0 -2px !important;
|
||||
}
|
||||
.selectize-control .selectize-input.disabled {
|
||||
opacity: 0.5;
|
||||
background-color: #fafafa;
|
||||
}
|
||||
@@ -0,0 +1,387 @@
|
||||
/**
|
||||
* selectize.default.css (v0.12.1) - Default Theme
|
||||
* Copyright (c) 2013–2015 Brian Reavis & contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
|
||||
* file except in compliance with the License. You may obtain a copy of the License at:
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under
|
||||
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
|
||||
* ANY KIND, either express or implied. See the License for the specific language
|
||||
* governing permissions and limitations under the License.
|
||||
*
|
||||
* @author Brian Reavis <brian@thirdroute.com>
|
||||
*/
|
||||
.selectize-control.plugin-drag_drop.multi > .selectize-input > div.ui-sortable-placeholder {
|
||||
visibility: visible !important;
|
||||
background: #f2f2f2 !important;
|
||||
background: rgba(0, 0, 0, 0.06) !important;
|
||||
border: 0 none !important;
|
||||
-webkit-box-shadow: inset 0 0 12px 4px #ffffff;
|
||||
box-shadow: inset 0 0 12px 4px #ffffff;
|
||||
}
|
||||
.selectize-control.plugin-drag_drop .ui-sortable-placeholder::after {
|
||||
content: '!';
|
||||
visibility: hidden;
|
||||
}
|
||||
.selectize-control.plugin-drag_drop .ui-sortable-helper {
|
||||
-webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
.selectize-dropdown-header {
|
||||
position: relative;
|
||||
padding: 5px 8px;
|
||||
border-bottom: 1px solid #d0d0d0;
|
||||
background: #f8f8f8;
|
||||
-webkit-border-radius: 3px 3px 0 0;
|
||||
-moz-border-radius: 3px 3px 0 0;
|
||||
border-radius: 3px 3px 0 0;
|
||||
}
|
||||
.selectize-dropdown-header-close {
|
||||
position: absolute;
|
||||
right: 8px;
|
||||
top: 50%;
|
||||
color: #303030;
|
||||
opacity: 0.4;
|
||||
margin-top: -12px;
|
||||
line-height: 20px;
|
||||
font-size: 20px !important;
|
||||
}
|
||||
.selectize-dropdown-header-close:hover {
|
||||
color: #000000;
|
||||
}
|
||||
.selectize-dropdown.plugin-optgroup_columns .optgroup {
|
||||
border-right: 1px solid #f2f2f2;
|
||||
border-top: 0 none;
|
||||
float: left;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.selectize-dropdown.plugin-optgroup_columns .optgroup:last-child {
|
||||
border-right: 0 none;
|
||||
}
|
||||
.selectize-dropdown.plugin-optgroup_columns .optgroup:before {
|
||||
display: none;
|
||||
}
|
||||
.selectize-dropdown.plugin-optgroup_columns .optgroup-header {
|
||||
border-top: 0 none;
|
||||
}
|
||||
.selectize-control.plugin-remove_button [data-value] {
|
||||
position: relative;
|
||||
padding-right: 24px !important;
|
||||
}
|
||||
.selectize-control.plugin-remove_button [data-value] .remove {
|
||||
z-index: 1;
|
||||
/* fixes ie bug (see #392) */
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 17px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
padding: 2px 0 0 0;
|
||||
border-left: 1px solid #0073bb;
|
||||
-webkit-border-radius: 0 2px 2px 0;
|
||||
-moz-border-radius: 0 2px 2px 0;
|
||||
border-radius: 0 2px 2px 0;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.selectize-control.plugin-remove_button [data-value] .remove:hover {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.selectize-control.plugin-remove_button [data-value].active .remove {
|
||||
border-left-color: #00578d;
|
||||
}
|
||||
.selectize-control.plugin-remove_button .disabled [data-value] .remove:hover {
|
||||
background: none;
|
||||
}
|
||||
.selectize-control.plugin-remove_button .disabled [data-value] .remove {
|
||||
border-left-color: #aaaaaa;
|
||||
}
|
||||
.selectize-control {
|
||||
position: relative;
|
||||
}
|
||||
.selectize-dropdown,
|
||||
.selectize-input,
|
||||
.selectize-input input {
|
||||
color: #303030;
|
||||
font-family: inherit;
|
||||
font-size: 13px;
|
||||
line-height: 18px;
|
||||
-webkit-font-smoothing: inherit;
|
||||
}
|
||||
.selectize-input,
|
||||
.selectize-control.single .selectize-input.input-active {
|
||||
background: #ffffff;
|
||||
cursor: text;
|
||||
display: inline-block;
|
||||
}
|
||||
.selectize-input {
|
||||
border: 1px solid #d0d0d0;
|
||||
padding: 8px 8px;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.1);
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.1);
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.selectize-control.multi .selectize-input.has-items {
|
||||
padding: 5px 8px 2px;
|
||||
}
|
||||
.selectize-input.full {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
.selectize-input.disabled,
|
||||
.selectize-input.disabled * {
|
||||
cursor: default !important;
|
||||
}
|
||||
.selectize-input.focus {
|
||||
-webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15);
|
||||
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
.selectize-input.dropdown-active {
|
||||
-webkit-border-radius: 3px 3px 0 0;
|
||||
-moz-border-radius: 3px 3px 0 0;
|
||||
border-radius: 3px 3px 0 0;
|
||||
}
|
||||
.selectize-input > * {
|
||||
vertical-align: baseline;
|
||||
display: -moz-inline-stack;
|
||||
display: inline-block;
|
||||
zoom: 1;
|
||||
*display: inline;
|
||||
}
|
||||
.selectize-control.multi .selectize-input > div {
|
||||
cursor: pointer;
|
||||
margin: 0 3px 3px 0;
|
||||
padding: 2px 6px;
|
||||
background: #1da7ee;
|
||||
color: #ffffff;
|
||||
border: 1px solid #0073bb;
|
||||
}
|
||||
.selectize-control.multi .selectize-input > div.active {
|
||||
background: #92c836;
|
||||
color: #ffffff;
|
||||
border: 1px solid #00578d;
|
||||
}
|
||||
.selectize-control.multi .selectize-input.disabled > div,
|
||||
.selectize-control.multi .selectize-input.disabled > div.active {
|
||||
color: #ffffff;
|
||||
background: #d2d2d2;
|
||||
border: 1px solid #aaaaaa;
|
||||
}
|
||||
.selectize-input > input {
|
||||
display: inline-block !important;
|
||||
padding: 0 !important;
|
||||
min-height: 0 !important;
|
||||
max-height: none !important;
|
||||
max-width: 100% !important;
|
||||
margin: 0 1px !important;
|
||||
text-indent: 0 !important;
|
||||
border: 0 none !important;
|
||||
background: none !important;
|
||||
line-height: inherit !important;
|
||||
-webkit-user-select: auto !important;
|
||||
-webkit-box-shadow: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
.selectize-input > input::-ms-clear {
|
||||
display: none;
|
||||
}
|
||||
.selectize-input > input:focus {
|
||||
outline: none !important;
|
||||
}
|
||||
.selectize-input::after {
|
||||
content: ' ';
|
||||
display: block;
|
||||
clear: left;
|
||||
}
|
||||
.selectize-input.dropdown-active::before {
|
||||
content: ' ';
|
||||
display: block;
|
||||
position: absolute;
|
||||
background: #f0f0f0;
|
||||
height: 1px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
.selectize-dropdown {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
border: 1px solid #d0d0d0;
|
||||
background: #ffffff;
|
||||
margin: -1px 0 0 0;
|
||||
border-top: 0 none;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
-webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
-webkit-border-radius: 0 0 3px 3px;
|
||||
-moz-border-radius: 0 0 3px 3px;
|
||||
border-radius: 0 0 3px 3px;
|
||||
}
|
||||
.selectize-dropdown [data-selectable] {
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
}
|
||||
.selectize-dropdown [data-selectable] .highlight {
|
||||
background: rgba(125, 168, 208, 0.2);
|
||||
-webkit-border-radius: 1px;
|
||||
-moz-border-radius: 1px;
|
||||
border-radius: 1px;
|
||||
}
|
||||
.selectize-dropdown [data-selectable],
|
||||
.selectize-dropdown .optgroup-header {
|
||||
padding: 5px 8px;
|
||||
}
|
||||
.selectize-dropdown .optgroup:first-child .optgroup-header {
|
||||
border-top: 0 none;
|
||||
}
|
||||
.selectize-dropdown .optgroup-header {
|
||||
color: #303030;
|
||||
background: #ffffff;
|
||||
cursor: default;
|
||||
}
|
||||
.selectize-dropdown .active {
|
||||
background-color: #f5fafd;
|
||||
color: #495c68;
|
||||
}
|
||||
.selectize-dropdown .active.create {
|
||||
color: #495c68;
|
||||
}
|
||||
.selectize-dropdown .create {
|
||||
color: rgba(48, 48, 48, 0.5);
|
||||
}
|
||||
.selectize-dropdown-content {
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
max-height: 200px;
|
||||
}
|
||||
.selectize-control.single .selectize-input,
|
||||
.selectize-control.single .selectize-input input {
|
||||
cursor: pointer;
|
||||
}
|
||||
.selectize-control.single .selectize-input.input-active,
|
||||
.selectize-control.single .selectize-input.input-active input {
|
||||
cursor: text;
|
||||
}
|
||||
.selectize-control.single .selectize-input:after {
|
||||
content: ' ';
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 15px;
|
||||
margin-top: -3px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-style: solid;
|
||||
border-width: 5px 5px 0 5px;
|
||||
border-color: #808080 transparent transparent transparent;
|
||||
}
|
||||
.selectize-control.single .selectize-input.dropdown-active:after {
|
||||
margin-top: -4px;
|
||||
border-width: 0 5px 5px 5px;
|
||||
border-color: transparent transparent #808080 transparent;
|
||||
}
|
||||
.selectize-control.rtl.single .selectize-input:after {
|
||||
left: 15px;
|
||||
right: auto;
|
||||
}
|
||||
.selectize-control.rtl .selectize-input > input {
|
||||
margin: 0 4px 0 -2px !important;
|
||||
}
|
||||
.selectize-control .selectize-input.disabled {
|
||||
opacity: 0.5;
|
||||
background-color: #fafafa;
|
||||
}
|
||||
.selectize-control.multi .selectize-input.has-items {
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
}
|
||||
.selectize-control.multi .selectize-input.disabled [data-value] {
|
||||
color: #999;
|
||||
text-shadow: none;
|
||||
background: none;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
.selectize-control.multi .selectize-input.disabled [data-value],
|
||||
.selectize-control.multi .selectize-input.disabled [data-value] .remove {
|
||||
border-color: #e6e6e6;
|
||||
}
|
||||
.selectize-control.multi .selectize-input.disabled [data-value] .remove {
|
||||
background: none;
|
||||
}
|
||||
.selectize-control.multi .selectize-input [data-value] {
|
||||
text-shadow: 0 1px 0 rgba(0, 51, 83, 0.3);
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
background-color: #1b9dec;
|
||||
background-image: -moz-linear-gradient(top, #1da7ee, #178ee9);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#1da7ee), to(#178ee9));
|
||||
background-image: -webkit-linear-gradient(top, #1da7ee, #178ee9);
|
||||
background-image: -o-linear-gradient(top, #1da7ee, #178ee9);
|
||||
background-image: linear-gradient(to bottom, #1da7ee, #178ee9);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff1da7ee', endColorstr='#ff178ee9', GradientType=0);
|
||||
-webkit-box-shadow: 0 1px 0 rgba(0,0,0,0.2),inset 0 1px rgba(255,255,255,0.03);
|
||||
box-shadow: 0 1px 0 rgba(0,0,0,0.2),inset 0 1px rgba(255,255,255,0.03);
|
||||
}
|
||||
.selectize-control.multi .selectize-input [data-value].active {
|
||||
background-color: #0085d4;
|
||||
background-image: -moz-linear-gradient(top, #008fd8, #0075cf);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#008fd8), to(#0075cf));
|
||||
background-image: -webkit-linear-gradient(top, #008fd8, #0075cf);
|
||||
background-image: -o-linear-gradient(top, #008fd8, #0075cf);
|
||||
background-image: linear-gradient(to bottom, #008fd8, #0075cf);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff008fd8', endColorstr='#ff0075cf', GradientType=0);
|
||||
}
|
||||
.selectize-control.single .selectize-input {
|
||||
-webkit-box-shadow: 0 1px 0 rgba(0,0,0,0.05), inset 0 1px 0 rgba(255,255,255,0.8);
|
||||
box-shadow: 0 1px 0 rgba(0,0,0,0.05), inset 0 1px 0 rgba(255,255,255,0.8);
|
||||
background-color: #f9f9f9;
|
||||
background-image: -moz-linear-gradient(top, #fefefe, #f2f2f2);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fefefe), to(#f2f2f2));
|
||||
background-image: -webkit-linear-gradient(top, #fefefe, #f2f2f2);
|
||||
background-image: -o-linear-gradient(top, #fefefe, #f2f2f2);
|
||||
background-image: linear-gradient(to bottom, #fefefe, #f2f2f2);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffefefe', endColorstr='#fff2f2f2', GradientType=0);
|
||||
}
|
||||
.selectize-control.single .selectize-input,
|
||||
.selectize-dropdown.single {
|
||||
border-color: #b8b8b8;
|
||||
}
|
||||
.selectize-dropdown .optgroup-header {
|
||||
padding-top: 7px;
|
||||
font-weight: bold;
|
||||
font-size: 0.85em;
|
||||
}
|
||||
.selectize-dropdown .optgroup {
|
||||
border-top: 1px solid #f0f0f0;
|
||||
}
|
||||
.selectize-dropdown .optgroup:first-child {
|
||||
border-top: 0 none;
|
||||
}
|
||||
@@ -0,0 +1,364 @@
|
||||
/**
|
||||
* selectize.legacy.css (v0.12.1) - Default Theme
|
||||
* Copyright (c) 2013–2015 Brian Reavis & contributors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
|
||||
* file except in compliance with the License. You may obtain a copy of the License at:
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under
|
||||
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
|
||||
* ANY KIND, either express or implied. See the License for the specific language
|
||||
* governing permissions and limitations under the License.
|
||||
*
|
||||
* @author Brian Reavis <brian@thirdroute.com>
|
||||
*/
|
||||
.selectize-control.plugin-drag_drop.multi > .selectize-input > div.ui-sortable-placeholder {
|
||||
visibility: visible !important;
|
||||
background: #f2f2f2 !important;
|
||||
background: rgba(0, 0, 0, 0.06) !important;
|
||||
border: 0 none !important;
|
||||
-webkit-box-shadow: inset 0 0 12px 4px #ffffff;
|
||||
box-shadow: inset 0 0 12px 4px #ffffff;
|
||||
}
|
||||
.selectize-control.plugin-drag_drop .ui-sortable-placeholder::after {
|
||||
content: '!';
|
||||
visibility: hidden;
|
||||
}
|
||||
.selectize-control.plugin-drag_drop .ui-sortable-helper {
|
||||
-webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
.selectize-dropdown-header {
|
||||
position: relative;
|
||||
padding: 7px 10px;
|
||||
border-bottom: 1px solid #d0d0d0;
|
||||
background: #f8f8f8;
|
||||
-webkit-border-radius: 3px 3px 0 0;
|
||||
-moz-border-radius: 3px 3px 0 0;
|
||||
border-radius: 3px 3px 0 0;
|
||||
}
|
||||
.selectize-dropdown-header-close {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 50%;
|
||||
color: #303030;
|
||||
opacity: 0.4;
|
||||
margin-top: -12px;
|
||||
line-height: 20px;
|
||||
font-size: 20px !important;
|
||||
}
|
||||
.selectize-dropdown-header-close:hover {
|
||||
color: #000000;
|
||||
}
|
||||
.selectize-dropdown.plugin-optgroup_columns .optgroup {
|
||||
border-right: 1px solid #f2f2f2;
|
||||
border-top: 0 none;
|
||||
float: left;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.selectize-dropdown.plugin-optgroup_columns .optgroup:last-child {
|
||||
border-right: 0 none;
|
||||
}
|
||||
.selectize-dropdown.plugin-optgroup_columns .optgroup:before {
|
||||
display: none;
|
||||
}
|
||||
.selectize-dropdown.plugin-optgroup_columns .optgroup-header {
|
||||
border-top: 0 none;
|
||||
}
|
||||
.selectize-control.plugin-remove_button [data-value] {
|
||||
position: relative;
|
||||
padding-right: 24px !important;
|
||||
}
|
||||
.selectize-control.plugin-remove_button [data-value] .remove {
|
||||
z-index: 1;
|
||||
/* fixes ie bug (see #392) */
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 17px;
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
padding: 1px 0 0 0;
|
||||
border-left: 1px solid #74b21e;
|
||||
-webkit-border-radius: 0 2px 2px 0;
|
||||
-moz-border-radius: 0 2px 2px 0;
|
||||
border-radius: 0 2px 2px 0;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.selectize-control.plugin-remove_button [data-value] .remove:hover {
|
||||
background: rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
.selectize-control.plugin-remove_button [data-value].active .remove {
|
||||
border-left-color: #6f9839;
|
||||
}
|
||||
.selectize-control.plugin-remove_button .disabled [data-value] .remove:hover {
|
||||
background: none;
|
||||
}
|
||||
.selectize-control.plugin-remove_button .disabled [data-value] .remove {
|
||||
border-left-color: #b4b4b4;
|
||||
}
|
||||
.selectize-control {
|
||||
position: relative;
|
||||
}
|
||||
.selectize-dropdown,
|
||||
.selectize-input,
|
||||
.selectize-input input {
|
||||
color: #303030;
|
||||
font-family: inherit;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
-webkit-font-smoothing: inherit;
|
||||
}
|
||||
.selectize-input,
|
||||
.selectize-control.single .selectize-input.input-active {
|
||||
background: #ffffff;
|
||||
cursor: text;
|
||||
display: inline-block;
|
||||
}
|
||||
.selectize-input {
|
||||
border: 1px solid #d0d0d0;
|
||||
padding: 10px 10px;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.1);
|
||||
box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.1);
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.selectize-control.multi .selectize-input.has-items {
|
||||
padding: 8px 10px 4px;
|
||||
}
|
||||
.selectize-input.full {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
.selectize-input.disabled,
|
||||
.selectize-input.disabled * {
|
||||
cursor: default !important;
|
||||
}
|
||||
.selectize-input.focus {
|
||||
-webkit-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15);
|
||||
box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
.selectize-input.dropdown-active {
|
||||
-webkit-border-radius: 3px 3px 0 0;
|
||||
-moz-border-radius: 3px 3px 0 0;
|
||||
border-radius: 3px 3px 0 0;
|
||||
}
|
||||
.selectize-input > * {
|
||||
vertical-align: baseline;
|
||||
display: -moz-inline-stack;
|
||||
display: inline-block;
|
||||
zoom: 1;
|
||||
*display: inline;
|
||||
}
|
||||
.selectize-control.multi .selectize-input > div {
|
||||
cursor: pointer;
|
||||
margin: 0 4px 4px 0;
|
||||
padding: 1px 5px;
|
||||
background: #b8e76f;
|
||||
color: #3d5d18;
|
||||
border: 1px solid #74b21e;
|
||||
}
|
||||
.selectize-control.multi .selectize-input > div.active {
|
||||
background: #92c836;
|
||||
color: #303030;
|
||||
border: 1px solid #6f9839;
|
||||
}
|
||||
.selectize-control.multi .selectize-input.disabled > div,
|
||||
.selectize-control.multi .selectize-input.disabled > div.active {
|
||||
color: #878787;
|
||||
background: #f8f8f8;
|
||||
border: 1px solid #b4b4b4;
|
||||
}
|
||||
.selectize-input > input {
|
||||
display: inline-block !important;
|
||||
padding: 0 !important;
|
||||
min-height: 0 !important;
|
||||
max-height: none !important;
|
||||
max-width: 100% !important;
|
||||
margin: 0 2px 0 0 !important;
|
||||
text-indent: 0 !important;
|
||||
border: 0 none !important;
|
||||
background: none !important;
|
||||
line-height: inherit !important;
|
||||
-webkit-user-select: auto !important;
|
||||
-webkit-box-shadow: none !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
.selectize-input > input::-ms-clear {
|
||||
display: none;
|
||||
}
|
||||
.selectize-input > input:focus {
|
||||
outline: none !important;
|
||||
}
|
||||
.selectize-input::after {
|
||||
content: ' ';
|
||||
display: block;
|
||||
clear: left;
|
||||
}
|
||||
.selectize-input.dropdown-active::before {
|
||||
content: ' ';
|
||||
display: block;
|
||||
position: absolute;
|
||||
background: #f0f0f0;
|
||||
height: 1px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
.selectize-dropdown {
|
||||
position: absolute;
|
||||
z-index: 10;
|
||||
border: 1px solid #d0d0d0;
|
||||
background: #ffffff;
|
||||
margin: -1px 0 0 0;
|
||||
border-top: 0 none;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
-webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
||||
-webkit-border-radius: 0 0 3px 3px;
|
||||
-moz-border-radius: 0 0 3px 3px;
|
||||
border-radius: 0 0 3px 3px;
|
||||
}
|
||||
.selectize-dropdown [data-selectable] {
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
}
|
||||
.selectize-dropdown [data-selectable] .highlight {
|
||||
background: rgba(255, 237, 40, 0.4);
|
||||
-webkit-border-radius: 1px;
|
||||
-moz-border-radius: 1px;
|
||||
border-radius: 1px;
|
||||
}
|
||||
.selectize-dropdown [data-selectable],
|
||||
.selectize-dropdown .optgroup-header {
|
||||
padding: 7px 10px;
|
||||
}
|
||||
.selectize-dropdown .optgroup:first-child .optgroup-header {
|
||||
border-top: 0 none;
|
||||
}
|
||||
.selectize-dropdown .optgroup-header {
|
||||
color: #303030;
|
||||
background: #f8f8f8;
|
||||
cursor: default;
|
||||
}
|
||||
.selectize-dropdown .active {
|
||||
background-color: #fffceb;
|
||||
color: #303030;
|
||||
}
|
||||
.selectize-dropdown .active.create {
|
||||
color: #303030;
|
||||
}
|
||||
.selectize-dropdown .create {
|
||||
color: rgba(48, 48, 48, 0.5);
|
||||
}
|
||||
.selectize-dropdown-content {
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
max-height: 200px;
|
||||
}
|
||||
.selectize-control.single .selectize-input,
|
||||
.selectize-control.single .selectize-input input {
|
||||
cursor: pointer;
|
||||
}
|
||||
.selectize-control.single .selectize-input.input-active,
|
||||
.selectize-control.single .selectize-input.input-active input {
|
||||
cursor: text;
|
||||
}
|
||||
.selectize-control.single .selectize-input:after {
|
||||
content: ' ';
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 15px;
|
||||
margin-top: -3px;
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-style: solid;
|
||||
border-width: 5px 5px 0 5px;
|
||||
border-color: #808080 transparent transparent transparent;
|
||||
}
|
||||
.selectize-control.single .selectize-input.dropdown-active:after {
|
||||
margin-top: -4px;
|
||||
border-width: 0 5px 5px 5px;
|
||||
border-color: transparent transparent #808080 transparent;
|
||||
}
|
||||
.selectize-control.rtl.single .selectize-input:after {
|
||||
left: 15px;
|
||||
right: auto;
|
||||
}
|
||||
.selectize-control.rtl .selectize-input > input {
|
||||
margin: 0 4px 0 -2px !important;
|
||||
}
|
||||
.selectize-control .selectize-input.disabled {
|
||||
opacity: 0.5;
|
||||
background-color: #fafafa;
|
||||
}
|
||||
.selectize-control.multi .selectize-input [data-value] {
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.1);
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
background-color: #b2e567;
|
||||
background-image: -moz-linear-gradient(top, #b8e76f, #a9e25c);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#b8e76f), to(#a9e25c));
|
||||
background-image: -webkit-linear-gradient(top, #b8e76f, #a9e25c);
|
||||
background-image: -o-linear-gradient(top, #b8e76f, #a9e25c);
|
||||
background-image: linear-gradient(to bottom, #b8e76f, #a9e25c);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffb8e76f', endColorstr='#ffa9e25c', GradientType=0);
|
||||
-webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
|
||||
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.selectize-control.multi .selectize-input [data-value].active {
|
||||
background-color: #88c332;
|
||||
background-image: -moz-linear-gradient(top, #92c836, #7abc2c);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#92c836), to(#7abc2c));
|
||||
background-image: -webkit-linear-gradient(top, #92c836, #7abc2c);
|
||||
background-image: -o-linear-gradient(top, #92c836, #7abc2c);
|
||||
background-image: linear-gradient(to bottom, #92c836, #7abc2c);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff92c836', endColorstr='#ff7abc2c', GradientType=0);
|
||||
}
|
||||
.selectize-control.single .selectize-input {
|
||||
-webkit-box-shadow: inset 0 1px 0 rgba(255,255,255,0.8), 0 2px 0 #e0e0e0, 0 3px 0 #c8c8c8, 0 4px 1px rgba(0,0,0,0.1);
|
||||
box-shadow: inset 0 1px 0 rgba(255,255,255,0.8), 0 2px 0 #e0e0e0, 0 3px 0 #c8c8c8, 0 4px 1px rgba(0,0,0,0.1);
|
||||
background-color: #f3f3f3;
|
||||
background-image: -moz-linear-gradient(top, #f5f5f5, #efefef);
|
||||
background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#efefef));
|
||||
background-image: -webkit-linear-gradient(top, #f5f5f5, #efefef);
|
||||
background-image: -o-linear-gradient(top, #f5f5f5, #efefef);
|
||||
background-image: linear-gradient(to bottom, #f5f5f5, #efefef);
|
||||
background-repeat: repeat-x;
|
||||
filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffefefef', GradientType=0);
|
||||
}
|
||||
.selectize-control.single .selectize-input,
|
||||
.selectize-dropdown.single {
|
||||
border-color: #b8b8b8;
|
||||
}
|
||||
.selectize-dropdown .optgroup-header {
|
||||
font-weight: bold;
|
||||
font-size: 0.8em;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
}
|
||||
68
backend/wordpress/wp-content/plugins/groups/groups.php
Normal file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
/**
|
||||
* groups.php
|
||||
*
|
||||
* Copyright (c) 2011-2018 "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.0.0
|
||||
*
|
||||
* Plugin Name: Groups
|
||||
* Plugin URI: http://www.itthinx.com/plugins/groups
|
||||
* Description: Groups provides group-based user membership management, group-based capabilities and content access control.
|
||||
* Version: 2.3.1
|
||||
* Author: itthinx
|
||||
* Author URI: http://www.itthinx.com
|
||||
* Donate-Link: http://www.itthinx.com
|
||||
* Text Domain: groups
|
||||
* Domain Path: /languages
|
||||
* License: GPLv3
|
||||
*/
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
define( 'GROUPS_CORE_VERSION', '2.3.1' );
|
||||
define( 'GROUPS_FILE', __FILE__ );
|
||||
if ( !defined( 'GROUPS_CORE_DIR' ) ) {
|
||||
define( 'GROUPS_CORE_DIR', untrailingslashit( plugin_dir_path( __FILE__ ) ) );
|
||||
}
|
||||
if ( !defined( 'GROUPS_CORE_LIB' ) ) {
|
||||
define( 'GROUPS_CORE_LIB', GROUPS_CORE_DIR . '/lib/core' );
|
||||
}
|
||||
if ( !defined( 'GROUPS_ACCESS_LIB' ) ) {
|
||||
define( 'GROUPS_ACCESS_LIB', GROUPS_CORE_DIR . '/lib/access' );
|
||||
}
|
||||
if ( !defined( 'GROUPS_ADMIN_LIB' ) ) {
|
||||
define( 'GROUPS_ADMIN_LIB', GROUPS_CORE_DIR . '/lib/admin' );
|
||||
}
|
||||
if ( !defined( 'GROUPS_AUTO_LIB' ) ) {
|
||||
define( 'GROUPS_AUTO_LIB', GROUPS_CORE_DIR . '/lib/auto' );
|
||||
}
|
||||
if ( !defined( 'GROUPS_VIEWS_LIB' ) ) {
|
||||
define( 'GROUPS_VIEWS_LIB', GROUPS_CORE_DIR . '/lib/views' );
|
||||
}
|
||||
if ( !defined( 'GROUPS_WP_LIB' ) ) {
|
||||
define( 'GROUPS_WP_LIB', GROUPS_CORE_DIR . '/lib/wp' );
|
||||
}
|
||||
if ( !defined( 'GROUPS_EXTRA_LIB' ) ) {
|
||||
define( 'GROUPS_EXTRA_LIB', GROUPS_CORE_DIR . '/lib/extra' );
|
||||
}
|
||||
if ( !defined( 'GROUPS_LEGACY_LIB' ) ) {
|
||||
define( 'GROUPS_LEGACY_LIB', GROUPS_CORE_DIR . '/legacy' );
|
||||
}
|
||||
if ( !defined( 'GROUPS_CORE_URL' ) ) {
|
||||
define( 'GROUPS_CORE_URL', plugins_url( 'groups' ) );
|
||||
}
|
||||
require_once( GROUPS_CORE_LIB . '/constants.php' );
|
||||
require_once( GROUPS_CORE_LIB . '/wp-init.php');
|
||||
|
After Width: | Height: | Size: 5.9 KiB |
|
After Width: | Height: | Size: 4.9 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 4.4 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 5.5 KiB |
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 3.3 KiB |
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 5.0 KiB |
BIN
backend/wordpress/wp-content/plugins/groups/images/add.png
Normal file
|
After Width: | Height: | Size: 220 B |
BIN
backend/wordpress/wp-content/plugins/groups/images/edit.png
Normal file
|
After Width: | Height: | Size: 244 B |
|
After Width: | Height: | Size: 7.3 KiB |
|
After Width: | Height: | Size: 302 B |
BIN
backend/wordpress/wp-content/plugins/groups/images/groups.png
Normal file
|
After Width: | Height: | Size: 549 B |
BIN
backend/wordpress/wp-content/plugins/groups/images/help.png
Normal file
|
After Width: | Height: | Size: 322 B |
BIN
backend/wordpress/wp-content/plugins/groups/images/refresh.png
Normal file
|
After Width: | Height: | Size: 298 B |
BIN
backend/wordpress/wp-content/plugins/groups/images/remove.png
Normal file
|
After Width: | Height: | Size: 223 B |
BIN
backend/wordpress/wp-content/plugins/groups/images/required.png
Normal file
|
After Width: | Height: | Size: 164 B |
3
backend/wordpress/wp-content/plugins/groups/js/selectize/selectize.min.js
vendored
Normal file
1307
backend/wordpress/wp-content/plugins/groups/languages/groups.pot
Normal file
@@ -0,0 +1,677 @@
|
||||
<?php
|
||||
/**
|
||||
* class-groups-access-meta-boxes-legacy.php
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 2.0.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
require_once( GROUPS_LEGACY_LIB . '/access/class-groups-post-access-legacy.php' );
|
||||
|
||||
/**
|
||||
* Adds meta boxes to edit screens.
|
||||
*
|
||||
* @link http://codex.wordpress.org/Function_Reference/add_meta_box
|
||||
*/
|
||||
class Groups_Access_Meta_Boxes_Legacy {
|
||||
|
||||
const NONCE = 'groups-meta-box-nonce-legacy';
|
||||
const SET_CAPABILITY = 'set-capability';
|
||||
const READ_ACCESS = 'read-access';
|
||||
const CAPABILITY = 'capability';
|
||||
const SHOW_GROUPS = 'access-meta-box-show-groups';
|
||||
|
||||
/**
|
||||
* Sets up an init hook where actions and filters are added.
|
||||
*/
|
||||
public static function init() {
|
||||
add_action( 'init', array( __CLASS__, 'wp_init' ) );
|
||||
add_action( 'admin_init', array( __CLASS__,'admin_init' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooks for capabilities meta box and saving options.
|
||||
*/
|
||||
public static function wp_init() {
|
||||
if ( current_user_can( GROUPS_ACCESS_GROUPS ) ) {
|
||||
require_once GROUPS_VIEWS_LIB . '/class-groups-uie.php';
|
||||
|
||||
add_action( 'add_meta_boxes', array( __CLASS__, 'add_meta_boxes' ), 10, 2 );
|
||||
add_action( 'save_post', array( __CLASS__, 'save_post' ), 10, 2 );
|
||||
add_filter( 'wp_insert_post_empty_content', array( __CLASS__, 'wp_insert_post_empty_content' ), 10, 2 );
|
||||
|
||||
add_filter( 'attachment_fields_to_edit', array( __CLASS__, 'attachment_fields_to_edit' ), 10, 2 );
|
||||
add_filter( 'attachment_fields_to_save', array( __CLASS__, 'attachment_fields_to_save' ), 10, 2 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooked on admin_init to register our action on admin_enqueue_scripts.
|
||||
*/
|
||||
public static function admin_init() {
|
||||
add_action( 'admin_enqueue_scripts', array( __CLASS__, 'admin_enqueue_scripts' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooked on admin_enqueue_scripts to timely enqueue resources required
|
||||
* on the media upload / attachment popup.
|
||||
*/
|
||||
public static function admin_enqueue_scripts() {
|
||||
global $pagenow;
|
||||
if ( $pagenow == 'upload.php' ) {
|
||||
Groups_UIE::enqueue( 'select' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggered by init() to add capability meta box.
|
||||
*/
|
||||
public static function add_meta_boxes( $post_type, $post = null ) {
|
||||
global $wp_version;
|
||||
$post_type_object = get_post_type_object( $post_type );
|
||||
if ( $post_type_object && $post_type != 'attachment' ) {
|
||||
$post_types_option = Groups_Options::get_option( Groups_Post_Access_Legacy::POST_TYPES, array() );
|
||||
if ( !isset( $post_types_option[$post_type]['add_meta_box'] ) || $post_types_option[$post_type]['add_meta_box'] ) {
|
||||
if ( $wp_version < 3.3 ) {
|
||||
$post_types = get_post_types();
|
||||
foreach ( $post_types as $post_type ) {
|
||||
add_meta_box(
|
||||
'groups-access',
|
||||
__( "Access restrictions", 'groups' ),
|
||||
array( __CLASS__, 'capability' ),
|
||||
$post_type,
|
||||
'side',
|
||||
'high'
|
||||
);
|
||||
}
|
||||
} else {
|
||||
add_meta_box(
|
||||
'groups-access',
|
||||
__( 'Access restrictions', 'groups' ),
|
||||
array( __CLASS__, 'capability' ),
|
||||
null,
|
||||
'side',
|
||||
'high'
|
||||
);
|
||||
}
|
||||
|
||||
Groups_UIE::enqueue( 'select' );
|
||||
|
||||
if ( current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
|
||||
if ( $screen = get_current_screen() ) {
|
||||
$screen->add_help_tab( array(
|
||||
'id' => 'groups-access',
|
||||
'title' => __( 'Access restrictions', 'groups' ),
|
||||
'content' =>
|
||||
'<p>' .
|
||||
'<strong>' . __( 'Access restrictions', 'groups' ) . '</strong>' .
|
||||
'</p>' .
|
||||
'<p>' .
|
||||
__( 'Use the <em>Access restrictions</em> box to limit the visibility of posts, pages and other post types.', 'groups' ) .
|
||||
'</p>' .
|
||||
'<p>' .
|
||||
__( 'You can select one or more capabilities that are enabled for access restriction.', 'groups' ) .
|
||||
' ' .
|
||||
__( 'Note that you must be a member of a group that has such a capability assigned.', 'groups' ) .
|
||||
'</p>' .
|
||||
'<p>' .
|
||||
'<strong>' . __( 'Example:', 'groups' ) . '</strong>' .
|
||||
'</p>' .
|
||||
__( 'Let\'s assume that you want to limit the visibility of a post to members of the <em>Premium</em> group.', 'groups' ) .
|
||||
'<p>' .
|
||||
'<strong>' . __( 'The quick way:', 'groups' ) . '</strong>' .
|
||||
' ' .
|
||||
__( 'Using the quick-create field', 'groups' ) .
|
||||
'</p>' .
|
||||
__( 'Enter <em>Premium</em> in the quick-create field located in the Access restrictions panel and save or update the post (or hit Enter).', 'groups' ) .
|
||||
'<p>' .
|
||||
'<p>' .
|
||||
__( 'Using the quick-create field, you can create a new group and capability. The capability will be assigned to the group and enabled to enforce read access. Group names are case-sensitive, the name of the capability is the lower-case version of the name of the group. If the group already exists, a new capability is created and assigned to the existing group. If the capability already exists, it will be assigned to the group. If both already exist, the capability is enabled to enforce read access. In order to be able to use the capability, your user account will be assigned to the group.', 'groups' ) .
|
||||
'</p>' .
|
||||
'<em>' . __( 'The manual way:', 'groups' ) . '</em>' .
|
||||
' ' .
|
||||
__( 'Adding the group and capability manually and enabling it for access restriction', 'groups' ) .
|
||||
'</p>' .
|
||||
'<p>' .
|
||||
__( 'Try the quick-create field first. Unless you need a more complex setup, there is no reason to go this way instead.', 'groups' ) .
|
||||
'</p>' .
|
||||
'<ol>' .
|
||||
'<li>' . __( 'Go to <strong>Groups > Groups</strong> and add the <em>Premium</em> group.', 'groups' ) . '</li>' .
|
||||
'<li>' . __( 'Go to <strong>Groups > Capabilities</strong> and add the <em>premium</em> capability.', 'groups' ) . '</li>' .
|
||||
'<li>' . __( 'Go to <strong>Groups > Groups</strong> and assign the <em>premium</em> capability to the <em>Premium</em> group.', 'groups' ) . '</li>' .
|
||||
'<li>' . __( 'Go to <strong>Groups > Options</strong> and enable the <em>premium</em> capability to restrict access.', 'groups' ) . '</li>' .
|
||||
'<li>' . __( 'Become a member of the <em>Premium</em> group - this is required so you can choose the <em>premium</em> capability to restrict access to a post.', 'groups' ) . '</li>' .
|
||||
'<li>' . __( 'Edit the post for which you want to restrict access and choose<sup>*</sup> the <em>premium</em> capability.', 'groups' ) . '</li>' .
|
||||
'</ol>' .
|
||||
'<p>' .
|
||||
__( '<sup>*</sup> For each capability, the groups that have the capability assigned are shown within parenthesis. You can choose a capability by typing part of the group\'s or the capability\'s name.', 'groups' ) .
|
||||
'</p>'
|
||||
) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render meta box for capabilities.
|
||||
*
|
||||
* @see do_meta_boxes()
|
||||
*
|
||||
* @param Object $object
|
||||
* @param Object $box
|
||||
*/
|
||||
public static function capability( $object = null, $box = null ) {
|
||||
|
||||
$output = '';
|
||||
|
||||
$show_groups = Groups_Options::get_user_option( self::SHOW_GROUPS, true );
|
||||
|
||||
$post_id = isset( $object->ID ) ? $object->ID : null;
|
||||
$post_type = isset( $object->post_type ) ? $object->post_type : null;
|
||||
$post_singular_name = __( 'Post', 'groups' );
|
||||
if ( $post_type !== null ) {
|
||||
$post_type_object = get_post_type_object( $post_type );
|
||||
$labels = isset( $post_type_object->labels ) ? $post_type_object->labels : null;
|
||||
if ( $labels !== null ) {
|
||||
if ( isset( $labels->singular_name ) ) {
|
||||
$post_singular_name = __( $labels->singular_name );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$output .= wp_nonce_field( self::SET_CAPABILITY, self::NONCE, true, false );
|
||||
|
||||
if ( self::user_can_restrict() ) {
|
||||
$user = new Groups_User( get_current_user_id() );
|
||||
$output .= __( 'Enforce read access', 'groups' );
|
||||
|
||||
$read_caps = get_post_meta( $post_id, Groups_Post_Access_Legacy::POSTMETA_PREFIX . Groups_Post_Access_Legacy::READ_POST_CAPABILITY );
|
||||
$valid_read_caps = Groups_Options::get_option( Groups_Post_Access_Legacy::READ_POST_CAPABILITIES, array( Groups_Post_Access_Legacy::READ_POST_CAPABILITY ) );
|
||||
$output .= '<div class="select-capability-container">';
|
||||
$output .= sprintf(
|
||||
'<select class="select capability" name="%s" multiple="multiple" placeholder="%s" data-placeholder="%s" title="%s">',
|
||||
self::CAPABILITY . '[]',
|
||||
__( 'Type and choose …', 'groups'),
|
||||
__( 'Type and choose …', 'groups'),
|
||||
__( 'Choose one or more capabilities to restrict access. Groups that grant access through the capabilities are shown in parenthesis. If no capabilities are available yet, you can use the quick-create box to create a group and capability enabled for access restriction on the fly.', 'groups' )
|
||||
);
|
||||
$output .= '<option value=""></option>';
|
||||
foreach( $valid_read_caps as $valid_read_cap ) {
|
||||
if ( $capability = Groups_Capability::read_by_capability( $valid_read_cap ) ) {
|
||||
if ( $user->can( $capability->capability ) ) {
|
||||
$c = new Groups_Capability( $capability->capability_id );
|
||||
$groups = $c->groups;
|
||||
$group_names = array();
|
||||
if ( !empty( $groups ) ) {
|
||||
foreach( $groups as $group ) {
|
||||
$group_names[] = $group->name;
|
||||
}
|
||||
}
|
||||
if ( count( $group_names ) > 0 ) {
|
||||
$label_title = sprintf(
|
||||
_n(
|
||||
'Members of the %1$s group can access this %2$s through this capability.',
|
||||
'Members of the %1$s groups can access this %2$s through this capability.',
|
||||
count( $group_names ),
|
||||
'groups'
|
||||
),
|
||||
wp_filter_nohtml_kses( implode( ',', $group_names ) ),
|
||||
$post_singular_name
|
||||
);
|
||||
} else {
|
||||
$label_title = __( 'No groups grant access through this capability. To grant access to group members using this capability, you should assign it to a group and enable the capability for access restriction.', 'groups' );
|
||||
}
|
||||
|
||||
$selected = apply_filters(
|
||||
'groups_access_restrictions_capability_selected',
|
||||
in_array( $capability->capability, $read_caps ),
|
||||
$capability->capability,
|
||||
$capability->capability_id,
|
||||
$read_caps,
|
||||
$post_id,
|
||||
$post_type
|
||||
);
|
||||
$output .= sprintf( '<option value="%s" %s>', esc_attr( $capability->capability_id ), $selected ? ' selected="selected" ': '' );
|
||||
$output .= wp_filter_nohtml_kses( $capability->capability );
|
||||
if ( $show_groups ) {
|
||||
if ( count( $group_names ) > 0 ) {
|
||||
$output .= ' ';
|
||||
$output .= '(' . wp_filter_nohtml_kses( implode( ', ', $group_names ) ) . ')';
|
||||
}
|
||||
}
|
||||
$output .= '</option>';
|
||||
}
|
||||
}
|
||||
}
|
||||
$output .= '</select>';
|
||||
|
||||
$output .= Groups_UIE::render_select( '.select.capability' );
|
||||
$output .= '</div>';
|
||||
|
||||
$output .= '<p class="description">';
|
||||
$output .= sprintf( __( "Only groups or users that have one of the selected capabilities are allowed to read this %s.", 'groups' ), $post_singular_name );
|
||||
$output .= '</p>';
|
||||
|
||||
$output .= '<p class="description">';
|
||||
$output .= sprintf( '<label title="%s">', __( 'Click to toggle the display of groups that grant the capabilities.', 'groups' ) );
|
||||
$output .= sprintf( '<input id="access-show-groups" type="checkbox" name="%s" %s />', esc_attr( self::SHOW_GROUPS ), $show_groups ? ' checked="checked" ' : '' );
|
||||
$output .= ' ';
|
||||
$output .= __( 'Show groups', 'groups' );
|
||||
$output .= '</label>';
|
||||
$output .= '</p>';
|
||||
$output .= '<script type="text/javascript">';
|
||||
$output .= 'if (typeof jQuery !== "undefined"){';
|
||||
$output .= !$show_groups ? 'jQuery("span.groups.description").hide();' : '';
|
||||
$output .= 'jQuery("#access-show-groups").click(function(){';
|
||||
$output .= 'jQuery("span.groups.description").toggle();';
|
||||
$output .= '});';
|
||||
$output .= '}';
|
||||
$output .= '</script>';
|
||||
} else {
|
||||
$output .= '<p class="description">';
|
||||
$output .= sprintf( __( 'You cannot set any access restrictions.', 'groups' ), $post_singular_name );
|
||||
$style = 'cursor:help;vertical-align:middle;';
|
||||
if ( current_user_can( GROUPS_ADMINISTER_OPTIONS ) ) {
|
||||
$style = 'cursor:pointer;vertical-align:middle;';
|
||||
$output .= sprintf( '<a href="%s">', esc_url( admin_url( 'admin.php?page=groups-admin-options' ) ) );
|
||||
}
|
||||
$output .= sprintf( '<img style="%s" alt="?" title="%s" src="%s" />', $style, esc_attr( __( 'You must be in a group that has at least one capability enabled to enforce read access.', 'groups' ) ), esc_attr( GROUPS_PLUGIN_URL . 'images/help.png' ) );
|
||||
if ( current_user_can( GROUPS_ADMINISTER_OPTIONS ) ) {
|
||||
$output .= '</a>';
|
||||
}
|
||||
$output .= '</p>';
|
||||
}
|
||||
|
||||
// quick-create
|
||||
if ( current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
|
||||
$style = 'cursor:help;vertical-align:middle;';
|
||||
$output .= '<div class="quick-create-group-capability" style="margin:4px 0">';
|
||||
$output .= '<label>';
|
||||
$output .= sprintf( '<input style="width:100%%;margin-right:-20px;" id="quick-group-capability" name="quick-group-capability" class="quick-group-capability" type="text" value="" placeholder="%s"/>', __( 'Quick-create group & capability', 'groups' ) );
|
||||
$output .= sprintf(
|
||||
'<img id="quick-create-help-icon" style="%s" alt="?" title="%s" src="%s" />',
|
||||
$style,
|
||||
esc_attr( __( 'You can create a new group and capability here. The capability will be assigned to the group and enabled to enforce read access. Group names are case-sensitive, the name of the capability is the lower-case version of the name of the group. If the group already exists, a new capability is created and assigned to the existing group. If the capability already exists, it will be assigned to the group. If both already exist, the capability is enabled to enforce read access. In order to be able to use the capability, your user account will be assigned to the group.', 'groups' ) ),
|
||||
esc_attr( GROUPS_PLUGIN_URL . 'images/help.png' )
|
||||
);
|
||||
$output .= '</label>';
|
||||
$output .= '</div>';
|
||||
$output .= '<script type="text/javascript">';
|
||||
$output .= 'if (typeof jQuery !== "undefined"){';
|
||||
$output .= 'jQuery("#quick-create-help-icon").click(function(){';
|
||||
$output .= 'jQuery("#contextual-help-link").click();';
|
||||
$output .= '});';
|
||||
$output .= '}';
|
||||
$output .= '</script>';
|
||||
}
|
||||
|
||||
echo $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes our save_post() if the post content is considered empty.
|
||||
* This is required because even on an empty post, we want to allow to
|
||||
* quick-create group and category as well as assign capabilities.
|
||||
* At WordPress 3.6.1, this is the only way we can achieve that, because
|
||||
* the save_post action is not invoked if the post content is considered
|
||||
* empty.
|
||||
*
|
||||
* @param boolean $maybe_empty
|
||||
* @param array $postarr
|
||||
* @return boolean
|
||||
*/
|
||||
public static function wp_insert_post_empty_content( $maybe_empty, $postarr ) {
|
||||
|
||||
// Only consider invoking save_post() here, if the post content is
|
||||
// considered to be empty at this stage. This is so we don't end up
|
||||
// having save_post() invoked twice when the post is not empty.
|
||||
if ( $maybe_empty ) {
|
||||
$post_id = !empty( $postarr['ID'] ) ? $postarr['ID'] : !empty( $postarr['post_ID'] ) ? $postarr['post_ID'] : null;
|
||||
if ( $post_id ) {
|
||||
self::save_post( $post_id );
|
||||
}
|
||||
}
|
||||
|
||||
return $maybe_empty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save capability options.
|
||||
*
|
||||
* @param int $post_id
|
||||
* @param mixed $post post data (not used here)
|
||||
*/
|
||||
public static function save_post( $post_id = null, $post = null ) {
|
||||
if ( ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) ) {
|
||||
} else {
|
||||
$post_type = get_post_type( $post_id );
|
||||
$post_type_object = get_post_type_object( $post_type );
|
||||
if ( $post_type_object && $post_type != 'attachment' ) {
|
||||
$post_types_option = Groups_Options::get_option( Groups_Post_Access_Legacy::POST_TYPES, array() );
|
||||
if ( !isset( $post_types_option[$post_type]['add_meta_box'] ) || $post_types_option[$post_type]['add_meta_box'] ) {
|
||||
if ( isset( $_POST[self::NONCE] ) && wp_verify_nonce( $_POST[self::NONCE], self::SET_CAPABILITY ) ) {
|
||||
$post_type = isset( $_POST['post_type'] ) ? $_POST['post_type'] : null;
|
||||
if ( $post_type !== null ) {
|
||||
// See http://codex.wordpress.org/Function_Reference/current_user_can 20130119 WP 3.5
|
||||
// "... Some capability checks (like 'edit_post' or 'delete_page') require this [the post ID] be provided."
|
||||
// If the post ID is not provided, it will throw:
|
||||
// PHP Notice: Undefined offset: 0 in /var/www/groups-forums/wp-includes/capabilities.php on line 1067
|
||||
$edit_post_type = 'edit_' . $post_type;
|
||||
if ( $post_type_object = get_post_type_object( $post_type ) ) {
|
||||
if ( !isset( $post_type_object->capabilities ) ) {
|
||||
// get_post_type_capabilities() (WP 3.8) will throw a warning
|
||||
// when trying to merge the missing property otherwise. It's either a
|
||||
// bug or the function's documentation should make it clear that you
|
||||
// have to provide that.
|
||||
$post_type_object->capabilities = array();
|
||||
}
|
||||
$caps_object = get_post_type_capabilities( $post_type_object );
|
||||
if ( isset( $caps_object->edit_post ) ) {
|
||||
$edit_post_type = $caps_object->edit_post;
|
||||
}
|
||||
}
|
||||
|
||||
if ( current_user_can( $edit_post_type, $post_id ) ) {
|
||||
// quick-create ?
|
||||
if ( current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
|
||||
if ( !empty( $_POST['quick-group-capability'] ) ) {
|
||||
$creator_id = get_current_user_id();
|
||||
$datetime = date( 'Y-m-d H:i:s', time() );
|
||||
$name = ucfirst( strtolower( trim( $_POST['quick-group-capability'] ) ) );
|
||||
if ( strlen( $name ) > 0 ) {
|
||||
// create or obtain the group
|
||||
if ( $group = Groups_Group::read_by_name( $name ) ) {
|
||||
} else {
|
||||
if ( $group_id = Groups_Group::create( compact( 'creator_id', 'datetime', 'name' ) ) ) {
|
||||
$group = Groups_Group::read( $group_id );
|
||||
}
|
||||
}
|
||||
// create or obtain the capability
|
||||
$name = strtolower( $name );
|
||||
if ( $capability = Groups_Capability::read_by_capability( $name ) ) {
|
||||
} else {
|
||||
if ( $capability_id = Groups_Capability::create( array( 'capability' => $name ) ) ) {
|
||||
$capability = Groups_Capability::read( $capability_id );
|
||||
}
|
||||
}
|
||||
if ( $group && $capability ) {
|
||||
// add the capability to the group
|
||||
if ( !Groups_Group_Capability::read( $group->group_id, $capability->capability_id ) ) {
|
||||
Groups_Group_Capability::create(
|
||||
array(
|
||||
'group_id' => $group->group_id,
|
||||
'capability_id' => $capability->capability_id
|
||||
)
|
||||
);
|
||||
}
|
||||
// enable the capability for access restriction
|
||||
$valid_read_caps = Groups_Options::get_option( Groups_Post_Access_Legacy::READ_POST_CAPABILITIES, array( Groups_Post_Access_Legacy::READ_POST_CAPABILITY ) );
|
||||
if ( !in_array( $capability->capability, $valid_read_caps ) ) {
|
||||
$valid_read_caps[] = $capability->capability;
|
||||
}
|
||||
Groups_Options::update_option( Groups_Post_Access_Legacy::READ_POST_CAPABILITIES, $valid_read_caps );
|
||||
// add the current user to the group
|
||||
Groups_User_Group::create(
|
||||
array(
|
||||
'user_id' => get_current_user_id(),
|
||||
'group_id' => $group->group_id
|
||||
)
|
||||
);
|
||||
// put the capability ID in $_POST[self::CAPABILITY] so it is treated below
|
||||
if ( empty( $_POST[self::CAPABILITY] ) ) {
|
||||
$_POST[self::CAPABILITY] = array();
|
||||
}
|
||||
if ( !in_array( $capability->capability_id, $_POST[self::CAPABILITY] ) ) {
|
||||
$_POST[self::CAPABILITY][] = $capability->capability_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// set
|
||||
if ( self::user_can_restrict() ) {
|
||||
$valid_read_caps = self::get_valid_read_caps_for_user();
|
||||
foreach( $valid_read_caps as $valid_read_cap ) {
|
||||
if ( $capability = Groups_Capability::read_by_capability( $valid_read_cap ) ) {
|
||||
if ( !empty( $_POST[self::CAPABILITY] ) && is_array( $_POST[self::CAPABILITY] ) && in_array( $capability->capability_id, $_POST[self::CAPABILITY] ) ) {
|
||||
Groups_Post_Access_Legacy::create( array(
|
||||
'post_id' => $post_id,
|
||||
'capability' => $capability->capability
|
||||
) );
|
||||
} else {
|
||||
Groups_Post_Access_Legacy::delete( $post_id, $capability->capability );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// show groups
|
||||
Groups_Options::update_user_option( self::SHOW_GROUPS, !empty( $_POST[self::SHOW_GROUPS] ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue scripts and styles.
|
||||
*/
|
||||
private static function enqueue() {
|
||||
global $groups_version;
|
||||
if ( !wp_script_is( 'selectize' ) ) {
|
||||
wp_enqueue_script( 'selectize', GROUPS_PLUGIN_URL . 'js/selectize/selectize.min.js', array( 'jquery' ), $groups_version, false );
|
||||
}
|
||||
if ( !wp_style_is( 'selectize' ) ) {
|
||||
wp_enqueue_style( 'selectize', GROUPS_PLUGIN_URL . 'css/selectize/selectize.bootstrap2.css', array(), $groups_version );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render capabilities box for attachment post type (Media).
|
||||
* @param array $form_fields
|
||||
* @param object $post
|
||||
* @return array
|
||||
*/
|
||||
public static function attachment_fields_to_edit( $form_fields, $post ) {
|
||||
|
||||
Groups_UIE::enqueue( 'select' );
|
||||
|
||||
$post_types_option = Groups_Options::get_option( Groups_Post_Access_Legacy::POST_TYPES, array() );
|
||||
if ( !isset( $post_types_option['attachment']['add_meta_box'] ) || $post_types_option['attachment']['add_meta_box'] ) {
|
||||
if ( self::user_can_restrict() ) {
|
||||
$user = new Groups_User( get_current_user_id() );
|
||||
$output = "";
|
||||
$post_singular_name = __( 'Media', 'groups' );
|
||||
|
||||
$output .= __( "Enforce read access", 'groups' );
|
||||
$read_caps = get_post_meta( $post->ID, Groups_Post_Access_Legacy::POSTMETA_PREFIX . Groups_Post_Access_Legacy::READ_POST_CAPABILITY );
|
||||
$valid_read_caps = self::get_valid_read_caps_for_user();
|
||||
|
||||
// On attachments edited within the 'Insert Media' popup, the update is triggered too soon and we end up with only the last capability selected.
|
||||
// This occurs when using normal checkboxes as well as the select below (Chosen and Selectize tested).
|
||||
// With checkboxes it's even more confusing, it's actually better to have it using a select as below,
|
||||
// because the visual feedback corresponds with what is assigned.
|
||||
// See http://wordpress.org/support/topic/multiple-access-restrictions-for-media-items-are-not-saved-in-grid-view
|
||||
// and https://core.trac.wordpress.org/ticket/28053 - this is an issue with multiple value fields and should
|
||||
// be fixed within WordPress.
|
||||
|
||||
// $output .= '<div style="padding:0 1em;margin:1em 0;border:1px solid #ccc;border-radius:4px;">';
|
||||
// $output .= '<ul>';
|
||||
// foreach( $valid_read_caps as $valid_read_cap ) {
|
||||
// if ( $capability = Groups_Capability::read_by_capability( $valid_read_cap ) ) {
|
||||
// $checked = in_array( $capability->capability, $read_caps ) ? ' checked="checked" ' : '';
|
||||
// $output .= '<li>';
|
||||
// $output .= '<label>';
|
||||
// $output .= '<input name="attachments[' . $post->ID . '][' . self::CAPABILITY . '][]" ' . $checked . ' type="checkbox" value="' . esc_attr( $capability->capability_id ) . '" />';
|
||||
// $output .= wp_filter_nohtml_kses( $capability->capability );
|
||||
// $output .= '</label>';
|
||||
// $output .= '</li>';
|
||||
// }
|
||||
// }
|
||||
// $output .= '</ul>';
|
||||
// $output .= '</div>';
|
||||
|
||||
$show_groups = Groups_Options::get_user_option( self::SHOW_GROUPS, true );
|
||||
$output .= '<div class="select-capability-container">';
|
||||
$select_id = 'attachments-' . $post->ID . '-' . self::CAPABILITY;
|
||||
$output .= sprintf(
|
||||
'<select id="%s" class="select capability" name="%s" multiple="multiple" data-placeholder="%s" title="%s">',
|
||||
$select_id,
|
||||
'attachments[' . $post->ID . '][' . self::CAPABILITY . '][]',
|
||||
__( 'Type and choose …', 'groups'),
|
||||
__( 'Choose one or more capabilities to restrict access. Groups that grant access through the capabilities are shown in parenthesis. If no capabilities are available yet, you can use the quick-create box to create a group and capability enabled for access restriction on the fly.', 'groups' )
|
||||
);
|
||||
$output .= '<option value=""></option>';
|
||||
foreach( $valid_read_caps as $valid_read_cap ) {
|
||||
if ( $capability = Groups_Capability::read_by_capability( $valid_read_cap ) ) {
|
||||
if ( $user->can( $capability->capability ) ) {
|
||||
$c = new Groups_Capability( $capability->capability_id );
|
||||
$groups = $c->groups;
|
||||
$group_names = array();
|
||||
if ( !empty( $groups ) ) {
|
||||
foreach( $groups as $group ) {
|
||||
$group_names[] = $group->name;
|
||||
}
|
||||
}
|
||||
if ( count( $group_names ) > 0 ) {
|
||||
$label_title = sprintf(
|
||||
_n(
|
||||
'Members of the %1$s group can access this %2$s through this capability.',
|
||||
'Members of the %1$s groups can access this %2$s through this capability.',
|
||||
count( $group_names ),
|
||||
'groups'
|
||||
),
|
||||
wp_filter_nohtml_kses( implode( ',', $group_names ) ),
|
||||
$post_singular_name
|
||||
);
|
||||
} else {
|
||||
$label_title = __( 'No groups grant access through this capability. To grant access to group members using this capability, you should assign it to a group and enable the capability for access restriction.', 'groups' );
|
||||
}
|
||||
$output .= sprintf( '<option value="%s" %s>', esc_attr( $capability->capability_id ), in_array( $capability->capability, $read_caps ) ? ' selected="selected" ' : '' );
|
||||
$output .= wp_filter_nohtml_kses( $capability->capability );
|
||||
if ( $show_groups ) {
|
||||
if ( count( $group_names ) > 0 ) {
|
||||
$output .= ' ';
|
||||
$output .= '(' . wp_filter_nohtml_kses( implode( ', ', $group_names ) ) . ')';
|
||||
}
|
||||
}
|
||||
$output .= '</option>';
|
||||
}
|
||||
}
|
||||
}
|
||||
$output .= '</select>';
|
||||
|
||||
$output .= Groups_UIE::render_select( '#'.$select_id );
|
||||
|
||||
$output .= '</div>';
|
||||
|
||||
$output .= '<p class="description">';
|
||||
$output .= sprintf( __( "Only groups or users that have one of the selected capabilities are allowed to read this %s.", 'groups' ), $post_singular_name );
|
||||
$output .= '</p>';
|
||||
|
||||
$form_fields['groups_access'] = array(
|
||||
'label' => __( 'Access restrictions', 'groups' ),
|
||||
'input' => 'html',
|
||||
'html' => $output
|
||||
);
|
||||
}
|
||||
}
|
||||
return $form_fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save capabilities for attachment post type (Media).
|
||||
* When multiple attachments are saved, this is called once for each.
|
||||
* @param array $post post data
|
||||
* @param array $attachment attachment field data
|
||||
* @return array
|
||||
*/
|
||||
public static function attachment_fields_to_save( $post, $attachment ) {
|
||||
$post_types_option = Groups_Options::get_option( Groups_Post_Access_Legacy::POST_TYPES, array() );
|
||||
if ( !isset( $post_types_option['attachment']['add_meta_box'] ) || $post_types_option['attachment']['add_meta_box'] ) {
|
||||
// if we're here, we assume the user is allowed to edit attachments,
|
||||
// but we still need to check if the user can restrict access
|
||||
if ( self::user_can_restrict() ) {
|
||||
$post_id = null;
|
||||
if ( isset( $post['ID'] ) ) {
|
||||
$post_id = $post['ID'];
|
||||
} else if ( isset( $post['post_ID'] ) ) {
|
||||
$post_id = $post['post_ID'];
|
||||
}
|
||||
if ( $post_id !== null ) {
|
||||
$valid_read_caps = self::get_valid_read_caps_for_user();
|
||||
foreach( $valid_read_caps as $valid_read_cap ) {
|
||||
if ( $capability = Groups_Capability::read_by_capability( $valid_read_cap ) ) {
|
||||
if ( !empty( $attachment[self::CAPABILITY] ) && is_array( $attachment[self::CAPABILITY] ) && in_array( $capability->capability_id, $attachment[self::CAPABILITY] ) ) {
|
||||
Groups_Post_Access_Legacy::create( array(
|
||||
'post_id' => $post_id,
|
||||
'capability' => $capability->capability
|
||||
) );
|
||||
} else {
|
||||
Groups_Post_Access_Legacy::delete( $post_id, $capability->capability );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $post;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the current user has at least one of the capabilities
|
||||
* that can be used to restrict access to posts.
|
||||
* @return boolean
|
||||
*/
|
||||
public static function user_can_restrict() {
|
||||
$has_read_cap = false;
|
||||
$user = new Groups_User( get_current_user_id() );
|
||||
$valid_read_caps = Groups_Options::get_option( Groups_Post_Access_Legacy::READ_POST_CAPABILITIES, array( Groups_Post_Access_Legacy::READ_POST_CAPABILITY ) );
|
||||
foreach( $valid_read_caps as $valid_read_cap ) {
|
||||
if ( $capability = Groups_Capability::read_by_capability( $valid_read_cap ) ) {
|
||||
if ( $user->can( $capability->capability_id ) ) {
|
||||
$has_read_cap = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $has_read_cap;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array of valid read capabilities for the current or given user
|
||||
*/
|
||||
public static function get_valid_read_caps_for_user( $user_id = null ) {
|
||||
$result = array();
|
||||
$user = new Groups_User( $user_id === null ? get_current_user_id() : $user_id );
|
||||
$valid_read_caps = Groups_Options::get_option( Groups_Post_Access_Legacy::READ_POST_CAPABILITIES, array( Groups_Post_Access_Legacy::READ_POST_CAPABILITY ) );
|
||||
foreach( $valid_read_caps as $valid_read_cap ) {
|
||||
if ( $capability = Groups_Capability::read_by_capability( $valid_read_cap ) ) {
|
||||
if ( $user->can( $capability->capability ) ) {
|
||||
$result[] = $valid_read_cap;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
Groups_Access_Meta_Boxes_Legacy::init();
|
||||
@@ -0,0 +1,424 @@
|
||||
<?php
|
||||
/**
|
||||
* class-groups-post-access-legacy.php
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 2.0.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Post access restrictions.
|
||||
*/
|
||||
class Groups_Post_Access_Legacy {
|
||||
|
||||
const POSTMETA_PREFIX = 'groups-';
|
||||
|
||||
const CACHE_GROUP = 'groups';
|
||||
const CAN_READ_POST = 'legacy_can_read_post';
|
||||
|
||||
const READ_POST_CAPABILITY = 'groups_read_post';
|
||||
const READ_POST_CAPABILITY_NAME = 'Read Post';
|
||||
const READ_POST_CAPABILITIES = 'read_post_capabilities';
|
||||
const POST_TYPES = 'post_types';
|
||||
|
||||
/**
|
||||
* Create needed capabilities on plugin activation.
|
||||
* Must be called explicitly or hooked into activation.
|
||||
*/
|
||||
public static function activate() {
|
||||
if ( !Groups_Capability::read_by_capability( self::READ_POST_CAPABILITY ) ) {
|
||||
Groups_Capability::create( array( 'capability' => self::READ_POST_CAPABILITY ) );
|
||||
// default read caps
|
||||
Groups_Options::update_option( Groups_Post_Access_Legacy::READ_POST_CAPABILITIES, array( Groups_Post_Access_Legacy::READ_POST_CAPABILITY ) );
|
||||
// for translation
|
||||
// @see self::READ_POST_CAPABILITY_NAME
|
||||
__( 'Read Post', 'groups' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up filters to restrict access.
|
||||
*/
|
||||
public static function init() {
|
||||
// Before Groups 2.0.0 this was called through Groups_Controller::activate() but
|
||||
// now we only need to create the capabilities if legacy is enabled.
|
||||
self::activate();
|
||||
// post access
|
||||
add_filter( 'posts_where', array( __CLASS__, 'posts_where' ), 10, 2 );
|
||||
add_filter( 'get_pages', array( __CLASS__, 'get_pages' ), 1 );
|
||||
if ( apply_filters( 'groups_filter_the_posts', false ) ) {
|
||||
add_filter( 'the_posts', array( __CLASS__, 'the_posts' ), 1, 2 );
|
||||
}
|
||||
add_filter( 'wp_get_nav_menu_items', array( __CLASS__, 'wp_get_nav_menu_items' ), 1, 3 );
|
||||
// content access
|
||||
add_filter( 'get_the_excerpt', array( __CLASS__, 'get_the_excerpt' ), 1 );
|
||||
add_filter( 'the_content', array( __CLASS__, 'the_content' ), 1 );
|
||||
// edit & delete post
|
||||
add_filter( 'map_meta_cap', array( __CLASS__, 'map_meta_cap' ), 10, 4 );
|
||||
add_action( 'groups_deleted_capability_capability', array( __CLASS__, 'groups_deleted_capability_capability' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Restrict access to edit or delete posts based on the post's access restrictions.
|
||||
*
|
||||
* @param array $caps
|
||||
* @param string $cap
|
||||
* @param int $user_id
|
||||
* @param array $args
|
||||
* @return array
|
||||
*/
|
||||
public static function map_meta_cap( $caps, $cap, $user_id, $args ) {
|
||||
if ( isset( $args[0] ) ) {
|
||||
if ( strpos( $cap, 'edit_' ) === 0 || strpos( $cap, 'delete_' ) === 0 ) {
|
||||
if ( $post_type = get_post_type( $args[0] ) ) {
|
||||
|
||||
$edit_post_type = 'edit_' . $post_type;
|
||||
$delete_post_type = 'delete_' . $post_type;
|
||||
if ( $post_type_object = get_post_type_object( $post_type ) ) {
|
||||
if ( !isset( $post_type_object->capabilities ) ) {
|
||||
$post_type_object->capabilities = array();
|
||||
}
|
||||
$caps_object = get_post_type_capabilities( $post_type_object );
|
||||
if ( isset( $caps_object->edit_post ) ) {
|
||||
$edit_post_type = $caps_object->edit_post;
|
||||
}
|
||||
if ( isset( $caps_object->delete_post ) ) {
|
||||
$delete_post_type = $caps_object->delete_post;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $cap === $edit_post_type || $cap === $delete_post_type ) {
|
||||
$post_id = null;
|
||||
if ( is_numeric( $args[0] ) ) {
|
||||
$post_id = $args[0];
|
||||
} else if ( $args[0] instanceof WP_Post ) {
|
||||
$post_id = $post->ID;
|
||||
}
|
||||
if ( $post_id ) {
|
||||
if ( !self::user_can_read_post( $post_id, $user_id ) ) {
|
||||
$caps[] = 'do_not_allow';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $caps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters out posts that the user should not be able to access.
|
||||
*
|
||||
* @param string $where current where conditions
|
||||
* @param WP_Query $query current query
|
||||
* @return string modified $where
|
||||
*/
|
||||
public static function posts_where( $where, $query ) {
|
||||
|
||||
global $wpdb;
|
||||
|
||||
$user_id = get_current_user_id();
|
||||
|
||||
// if administrators can override access, don't filter
|
||||
if ( _groups_admin_override() ) {
|
||||
return $where;
|
||||
}
|
||||
|
||||
// 1. Get all the capabilities that the user has, including those that are inherited:
|
||||
$caps = array();
|
||||
if ( $user = new Groups_User( $user_id ) ) {
|
||||
$capabilities = $user->capabilities_deep;
|
||||
if ( is_array( $capabilities ) ) {
|
||||
foreach ( $capabilities as $capability ) {
|
||||
$caps[] = "'". $capability . "'";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( count( $caps ) > 0 ) {
|
||||
$caps = implode( ',', $caps );
|
||||
} else {
|
||||
$caps = '\'\'';
|
||||
}
|
||||
|
||||
// 2. Filter the posts that require a capability that the user doesn't
|
||||
// have, or in other words: exclude posts that the user must NOT access:
|
||||
|
||||
// The following is not correct in that it requires the user to have ALL capabilities:
|
||||
// $where .= sprintf(
|
||||
// " AND {$wpdb->posts}.ID NOT IN (SELECT DISTINCT ID FROM $wpdb->posts LEFT JOIN $wpdb->postmeta on {$wpdb->posts}.ID = {$wpdb->postmeta}.post_id WHERE {$wpdb->postmeta}.meta_key = '%s' AND {$wpdb->postmeta}.meta_value NOT IN (%s) ) ",
|
||||
// self::POSTMETA_PREFIX . self::READ_POST_CAPABILITY,
|
||||
// $caps
|
||||
// );
|
||||
|
||||
// This allows the user to access posts where the posts are not restricted or where
|
||||
// the user has ANY of the capabilities:
|
||||
$where .= sprintf(
|
||||
" AND {$wpdb->posts}.ID IN " .
|
||||
" ( " .
|
||||
" SELECT ID FROM $wpdb->posts WHERE ID NOT IN ( SELECT post_id FROM $wpdb->postmeta WHERE {$wpdb->postmeta}.meta_key = '%s' ) " . // posts without access restriction
|
||||
" UNION ALL " . // we don't care about duplicates here, just make it quick
|
||||
" SELECT post_id AS ID FROM $wpdb->postmeta WHERE {$wpdb->postmeta}.meta_key = '%s' AND {$wpdb->postmeta}.meta_value IN (%s) " . // posts that require any capability the user has
|
||||
" ) ",
|
||||
self::POSTMETA_PREFIX . self::READ_POST_CAPABILITY,
|
||||
self::POSTMETA_PREFIX . self::READ_POST_CAPABILITY,
|
||||
$caps
|
||||
);
|
||||
|
||||
return $where;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter pages by access capability.
|
||||
*
|
||||
* @param array $pages
|
||||
*/
|
||||
public static function get_pages( $pages ) {
|
||||
$result = array();
|
||||
$user_id = get_current_user_id();
|
||||
foreach ( $pages as $page ) {
|
||||
if ( self::user_can_read_post( $page->ID, $user_id ) ) {
|
||||
$result[] = $page;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter posts by access capability.
|
||||
*
|
||||
* @param array $posts list of posts
|
||||
* @param WP_Query $query
|
||||
*/
|
||||
public static function the_posts( $posts, &$query ) {
|
||||
$result = array();
|
||||
$user_id = get_current_user_id();
|
||||
foreach ( $posts as $post ) {
|
||||
if ( self::user_can_read_post( $post->ID, $user_id ) ) {
|
||||
$result[] = $post;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter menu items by access capability.
|
||||
*
|
||||
* @param array $items
|
||||
* @param mixed $menu
|
||||
* @param array $args
|
||||
*/
|
||||
public static function wp_get_nav_menu_items( $items = null, $menu = null, $args = null ) {
|
||||
$result = array();
|
||||
$user_id = get_current_user_id();
|
||||
foreach ( $items as $item ) {
|
||||
if ( self::user_can_read_post( $item->object_id, $user_id ) ) {
|
||||
$result[] = $item;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter excerpt by access capability.
|
||||
*
|
||||
* @param string $output
|
||||
* @return $output if access granted, otherwise ''
|
||||
*/
|
||||
public static function get_the_excerpt( $output ) {
|
||||
global $post;
|
||||
$result = '';
|
||||
if ( isset( $post->ID ) ) {
|
||||
if ( self::user_can_read_post( $post->ID ) ) {
|
||||
$result = $output;
|
||||
}
|
||||
} else {
|
||||
// not a post, don't interfere
|
||||
$result = $output;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter content by access capability.
|
||||
*
|
||||
* @param string $output
|
||||
* @return $output if access granted, otherwise ''
|
||||
*/
|
||||
public static function the_content( $output ) {
|
||||
global $post;
|
||||
$result = '';
|
||||
if ( isset( $post->ID ) ) {
|
||||
if ( self::user_can_read_post( $post->ID ) ) {
|
||||
$result = $output;
|
||||
}
|
||||
} else {
|
||||
// not a post, don't interfere
|
||||
$result = $output;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an access capability requirement.
|
||||
*
|
||||
* $map must contain 'post_id' (*)
|
||||
*
|
||||
* For now this only should be used to add the READ_POST_CAPABILITY which
|
||||
* it does automatically. Nothing else is checked for granting access.
|
||||
*
|
||||
* (*) Revisions : As of Groups 1.3.13 and at WordPress 3.6.1, as
|
||||
* add_post_meta stores postmeta for the revision's parent, we retrieve
|
||||
* the parent's post ID if it applies and check against that to see if
|
||||
* that capability is already present. This is to avoid duplicating
|
||||
* the already existing postmeta entry (which ocurred in previous
|
||||
* versions).
|
||||
*
|
||||
* @param array $map
|
||||
* @return true if the capability could be added to the post, otherwis false
|
||||
*/
|
||||
public static function create( $map ) {
|
||||
extract( $map );
|
||||
$result = false;
|
||||
|
||||
if ( !isset( $capability ) ) {
|
||||
$capability = self::READ_POST_CAPABILITY;
|
||||
}
|
||||
|
||||
if ( !empty( $post_id ) && !empty( $capability) ) {
|
||||
if ( Groups_Capability::read_by_capability( $capability ) ) {
|
||||
if ( $revision_parent_id = wp_is_post_revision( $post_id ) ) {
|
||||
$post_id = $revision_parent_id;
|
||||
}
|
||||
if ( !in_array( $capability, get_post_meta( $post_id, self::POSTMETA_PREFIX . self::READ_POST_CAPABILITY ) ) ) {
|
||||
$result = add_post_meta( $post_id, self::POSTMETA_PREFIX . self::READ_POST_CAPABILITY, $capability );
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the post requires the given capability to grant access.
|
||||
*
|
||||
* Currently only READ_POST_CAPABILITY should be used, this is also taken
|
||||
* as the default.
|
||||
*
|
||||
* @param int $post_id
|
||||
* @param string $capability capability label
|
||||
* @return true if the capability is required, otherwise false
|
||||
*/
|
||||
public static function read( $post_id, $capability = self::READ_POST_CAPABILITY ) {
|
||||
$result = false;
|
||||
$caps = get_post_meta( $post_id, self::POSTMETA_PREFIX . self::READ_POST_CAPABILITY );
|
||||
if ( $caps ) {
|
||||
$result = in_array( $capability, $caps );
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Currently does nothing, always returns false.
|
||||
*
|
||||
* @param array $map
|
||||
* @return false
|
||||
*/
|
||||
public static function update( $map ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a capability requirement from a post.
|
||||
*
|
||||
* @param int $post_id
|
||||
* @param string $capability defaults to groups_read_post, removes all if null is given
|
||||
* @return true on success, otherwise false
|
||||
*/
|
||||
public static function delete( $post_id, $capability = self::READ_POST_CAPABILITY ) {
|
||||
$result = false;
|
||||
if ( !empty( $post_id ) ) {
|
||||
if ( !empty( $capability ) ) {
|
||||
$result = delete_post_meta( $post_id, self::POSTMETA_PREFIX . self::READ_POST_CAPABILITY, $capability );
|
||||
} else {
|
||||
$result = delete_post_meta( $post_id, self::POSTMETA_PREFIX . self::READ_POST_CAPABILITY );
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of capabilities that grant access to the post.
|
||||
*
|
||||
* @param int $post_id
|
||||
* @return array of string, capabilities
|
||||
*/
|
||||
public static function get_read_post_capabilities( $post_id ) {
|
||||
return get_post_meta( $post_id, self::POSTMETA_PREFIX . self::READ_POST_CAPABILITY );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the user has any of the capabilities that grant access to the post.
|
||||
*
|
||||
* @param int $post_id post id
|
||||
* @param int $user_id user id or null for current user
|
||||
* @return boolean true if user can read the post
|
||||
*/
|
||||
public static function user_can_read_post( $post_id, $user_id = null ) {
|
||||
$result = false;
|
||||
if ( !empty( $post_id ) ) {
|
||||
if ( $user_id === null ) {
|
||||
$user_id = get_current_user_id();
|
||||
}
|
||||
$cached = Groups_Cache::get( self::CAN_READ_POST . '_' . $user_id . '_' . $post_id, self::CACHE_GROUP );
|
||||
if ( $cached !== null ) {
|
||||
$result = $cached->value;
|
||||
unset( $cached ) ;
|
||||
} else {
|
||||
$groups_user = new Groups_User( $user_id );
|
||||
$read_caps = self::get_read_post_capabilities( $post_id );
|
||||
if ( !empty( $read_caps ) ) {
|
||||
foreach( $read_caps as $read_cap ) {
|
||||
if ( $groups_user->can( $read_cap ) ) {
|
||||
$result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$result = true;
|
||||
}
|
||||
$result = apply_filters( 'groups_post_access_user_can_read_post', $result, $post_id, $user_id );
|
||||
Groups_Cache::set( self::CAN_READ_POST . '_' . $user_id . '_' . $post_id, $result, self::CACHE_GROUP );
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooks into groups_deleted_capability_capability to remove existing access
|
||||
* restrictions based on the deleted capability.
|
||||
*
|
||||
* @param string $name of the deleted capability
|
||||
*/
|
||||
public static function groups_deleted_capability_capability( $capability ) {
|
||||
delete_metadata( 'post', null, self::POSTMETA_PREFIX . self::READ_POST_CAPABILITY, $capability, true );
|
||||
}
|
||||
|
||||
}
|
||||
Groups_Post_Access_Legacy::init();
|
||||
@@ -0,0 +1,116 @@
|
||||
<?php
|
||||
/**
|
||||
* class-groups-admin-custom-posts-legacy.php
|
||||
*
|
||||
* Copyright (c) 2012 "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Antonio Blanco
|
||||
* @package groups
|
||||
* @since groups 2.0.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Post column extensions.
|
||||
*/
|
||||
class Groups_Admin_Post_Columns_Legacy {
|
||||
|
||||
const CAPABILITIES = 'capabilities';
|
||||
|
||||
/**
|
||||
* Adds an admin_init action.
|
||||
*/
|
||||
public static function init() {
|
||||
add_action( 'admin_init', array( __CLASS__, 'admin_init' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the filters and actions only for users who have the right
|
||||
* Groups permissions and for the post types that have access
|
||||
* restrictions enabled.
|
||||
*/
|
||||
public static function admin_init() {
|
||||
if ( current_user_can( GROUPS_ACCESS_GROUPS ) ) {
|
||||
$post_types = get_post_types( array( 'public' => true ) );
|
||||
$post_types_option = Groups_Options::get_option( Groups_Post_Access_Legacy::POST_TYPES, array() );
|
||||
foreach ( $post_types as $post_type ) {
|
||||
if ( !isset( $post_types_option[$post_type]['add_meta_box'] ) || $post_types_option[$post_type]['add_meta_box'] ) {
|
||||
if ( ( $post_type == 'attachment' ) ) {
|
||||
// filters to display the media's access restriction capabilities
|
||||
add_filter( 'manage_media_columns', array( __CLASS__, 'columns' ) );
|
||||
// args: string $column_name, int $media_id
|
||||
add_action( 'manage_media_custom_column', array( __CLASS__, 'custom_column' ), 10, 2 );
|
||||
} else {
|
||||
// filters to display the posts' access restriction capabilities
|
||||
add_filter( 'manage_' . $post_type . '_posts_columns', array( __CLASS__, 'columns' ) );
|
||||
// args: string $column_name, int $post_id
|
||||
add_action( 'manage_' . $post_type . '_posts_custom_column', array( __CLASS__, 'custom_column' ), 10, 2 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new column to the post type's table showing the access
|
||||
* restriction capabilities.
|
||||
*
|
||||
* @param array $column_headers
|
||||
* @return array column headers
|
||||
*/
|
||||
public static function columns( $column_headers ) {
|
||||
$column_headers[self::CAPABILITIES] = sprintf(
|
||||
__( '<span title="%s">Access Restrictions</span>', 'groups' ),
|
||||
esc_attr( __( 'One or more capabilities required to read the entry.', 'groups' ) )
|
||||
);
|
||||
return $column_headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders custom column content.
|
||||
*
|
||||
* @param string $column_name
|
||||
* @param int $post_id
|
||||
* @return string custom column content
|
||||
*/
|
||||
public static function custom_column( $column_name, $post_id ) {
|
||||
$output = '';
|
||||
switch ( $column_name ) {
|
||||
case self::CAPABILITIES :
|
||||
$read_caps = get_post_meta( $post_id, Groups_Post_Access_Legacy::POSTMETA_PREFIX . Groups_Post_Access_Legacy::READ_POST_CAPABILITY );
|
||||
$valid_read_caps = Groups_Options::get_option( Groups_Post_Access_Legacy::READ_POST_CAPABILITIES, array( Groups_Post_Access_Legacy::READ_POST_CAPABILITY ) );
|
||||
if ( count( $valid_read_caps ) > 0 ) {
|
||||
sort( $valid_read_caps );
|
||||
$output = '<ul>';
|
||||
foreach( $valid_read_caps as $valid_read_cap ) {
|
||||
if ( $capability = Groups_Capability::read_by_capability( $valid_read_cap ) ) {
|
||||
if ( in_array( $valid_read_cap, $read_caps ) ) {
|
||||
$output .= '<li>';
|
||||
$output .= wp_strip_all_tags( $capability->capability );
|
||||
$output .= '</li>';
|
||||
}
|
||||
}
|
||||
}
|
||||
$output .= '</ul>';
|
||||
} else {
|
||||
$output .= '';
|
||||
}
|
||||
break;
|
||||
}
|
||||
echo $output;
|
||||
}
|
||||
}
|
||||
Groups_Admin_Post_Columns_Legacy::init();
|
||||
@@ -0,0 +1,343 @@
|
||||
<?php
|
||||
/**
|
||||
* class-groups-admin-posts-legacy.php
|
||||
*
|
||||
* Copyright (c) 2013 "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 2.0.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Additions to post overview admin screens.
|
||||
*/
|
||||
class Groups_Admin_Posts_Legacy {
|
||||
|
||||
const NOT_RESTRICTED = '#not-restricted#';
|
||||
|
||||
/**
|
||||
* Sets up an admin_init hook where our actions and filters are added.
|
||||
*/
|
||||
public static function init() {
|
||||
add_action( 'admin_init', array( __CLASS__, 'admin_init' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds actions and filters to handle filtering by access restriction
|
||||
* capability.
|
||||
*/
|
||||
public static function admin_init() {
|
||||
if ( current_user_can( GROUPS_ACCESS_GROUPS ) ) {
|
||||
add_action( 'admin_enqueue_scripts', array( __CLASS__, 'admin_enqueue_scripts' ) );
|
||||
add_action( 'admin_head', array( __CLASS__, 'admin_head' ) );
|
||||
add_action( 'restrict_manage_posts', array( __CLASS__, 'restrict_manage_posts' ) );
|
||||
add_filter( 'parse_query', array( __CLASS__, 'parse_query' ) );
|
||||
|
||||
add_action( 'bulk_edit_custom_box', array( __CLASS__, 'bulk_edit_custom_box' ), 10, 2);
|
||||
add_action( 'save_post', array( __CLASS__, 'save_post' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues the select script.
|
||||
*/
|
||||
public static function admin_enqueue_scripts() {
|
||||
|
||||
global $pagenow;
|
||||
|
||||
if ( $pagenow == 'edit.php' ) {
|
||||
$post_type = isset( $_GET['post_type'] ) ? $_GET['post_type'] : 'post';
|
||||
$post_types_option = Groups_Options::get_option( Groups_Post_Access_Legacy::POST_TYPES, array() );
|
||||
if ( !isset( $post_types_option[$post_type]['add_meta_box'] ) || $post_types_option[$post_type]['add_meta_box'] ) {
|
||||
Groups_UIE::enqueue( 'select' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds CSS rules to display our access restriction filter coherently.
|
||||
*/
|
||||
public static function admin_head() {
|
||||
|
||||
global $pagenow;
|
||||
|
||||
if ( $pagenow == 'edit.php' ) {
|
||||
$post_type = isset( $_GET['post_type'] ) ? $_GET['post_type'] : 'post';
|
||||
$post_types_option = Groups_Options::get_option( Groups_Post_Access_Legacy::POST_TYPES, array() );
|
||||
if ( !isset( $post_types_option[$post_type]['add_meta_box'] ) || $post_types_option[$post_type]['add_meta_box'] ) {
|
||||
echo '<style type="text/css">';
|
||||
echo '.groups-capabilities-container { display: inline-block; line-height: 24px; padding-bottom: 1em; vertical-align: top; margin-left: 4px; margin-right: 4px; }';
|
||||
echo '.groups-capabilities-container .groups-select-container { display: inline-block; vertical-align: top; }';
|
||||
echo '.groups-capabilities-container .groups-select-container select, .groups-bulk-container select.groups-action { float: none; margin-right: 4px; vertical-align: top; }';
|
||||
echo '.groups-capabilities-container .selectize-control { min-width: 128px; }';
|
||||
echo '.groups-capabilities-container .selectize-control, .groups-bulk-container select.groups-action { margin-right: 4px; vertical-align: top; }';
|
||||
echo '.groups-capabilities-container .selectize-input { font-size: inherit; line-height: 18px; padding: 1px 2px 2px 2px; vertical-align: middle; }';
|
||||
echo '.groups-capabilities-container .selectize-input input[type="text"] { font-size: inherit; vertical-align: middle; }';
|
||||
echo '.groups-capabilities-container input.button { margin-top: 1px; vertical-align: top; }';
|
||||
echo '.inline-edit-row fieldset .capabilities-bulk-container label span.title { min-width: 5em; padding: 2px 1em; width: auto; }';
|
||||
echo '.tablenav .actions { overflow: visible; }'; // this is important so that the selectize options aren't hidden
|
||||
echo '.wp-list-table td { overflow: visible; }'; // idem for bulk actions
|
||||
echo '</style>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the access restriction field.
|
||||
*/
|
||||
public static function restrict_manage_posts() {
|
||||
|
||||
global $pagenow, $wpdb;
|
||||
|
||||
if ( is_admin() ) {
|
||||
|
||||
if ( $pagenow == 'edit.php' ) { // check that we're on the right screen
|
||||
|
||||
$post_type = isset( $_GET['post_type'] ) ? $_GET['post_type'] : 'post';
|
||||
$post_types_option = Groups_Options::get_option( Groups_Post_Access_Legacy::POST_TYPES, array() );
|
||||
|
||||
if ( !isset( $post_types_option[$post_type]['add_meta_box'] ) || $post_types_option[$post_type]['add_meta_box'] ) {
|
||||
|
||||
$output = '';
|
||||
|
||||
// capabilities select
|
||||
$output .= '<div class="groups-capabilities-container">';
|
||||
$applicable_read_caps = Groups_Options::get_option( Groups_Post_Access_Legacy::READ_POST_CAPABILITIES, array( Groups_Post_Access_Legacy::READ_POST_CAPABILITY ) );
|
||||
$output .= sprintf(
|
||||
'<select class="select capability" name="%s[]" multiple="multiple" placeholder="%s" data-placeholder="%s">',
|
||||
esc_attr( Groups_Post_Access_Legacy::POSTMETA_PREFIX . Groups_Post_Access_Legacy::READ_POST_CAPABILITY ),
|
||||
esc_attr( __( 'Access restrictions …', 'groups' ) ) ,
|
||||
esc_attr( __( 'Access restrictions …', 'groups' ) )
|
||||
);
|
||||
|
||||
$previous_selected = array();
|
||||
if ( !empty( $_GET[Groups_Post_Access_Legacy::POSTMETA_PREFIX . Groups_Post_Access_Legacy::READ_POST_CAPABILITY] ) ) {
|
||||
$previous_selected = $_GET[Groups_Post_Access_Legacy::POSTMETA_PREFIX . Groups_Post_Access_Legacy::READ_POST_CAPABILITY];
|
||||
if ( !is_array( $previous_selected ) ) {
|
||||
$previous_selected = array();
|
||||
}
|
||||
}
|
||||
$selected = in_array( self::NOT_RESTRICTED, $previous_selected ) ? ' selected="selected" ' : '';
|
||||
$output .= sprintf( '<option value="%s" %s >%s</option>', self::NOT_RESTRICTED, esc_attr( $selected ), esc_attr( __( '(only unrestricted)', 'groups' ) ) );
|
||||
|
||||
foreach( $applicable_read_caps as $capability ) {
|
||||
$selected = in_array( $capability, $previous_selected ) ? ' selected="selected" ' : '';
|
||||
$output .= sprintf( '<option value="%s" %s >%s</option>', esc_attr( $capability ), esc_attr( $selected ), wp_filter_nohtml_kses( $capability ) );
|
||||
}
|
||||
$output .= '</select>';
|
||||
$output .= '</div>';
|
||||
$output .= Groups_UIE::render_select( '.select.capability' );
|
||||
|
||||
echo $output;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk-edit access restriction capabilities.
|
||||
*
|
||||
* @param string $column_name
|
||||
* @param string $post_type
|
||||
*/
|
||||
public static function bulk_edit_custom_box( $column_name, $post_type ) {
|
||||
|
||||
global $pagenow, $wpdb;
|
||||
|
||||
if ( $column_name == 'capabilities' ) {
|
||||
|
||||
if ( $pagenow == 'edit.php' ) { // check that we're on the right screen
|
||||
|
||||
$post_type = isset( $_GET['post_type'] ) ? $_GET['post_type'] : 'post';
|
||||
$post_types_option = Groups_Options::get_option( Groups_Post_Access_Legacy::POST_TYPES, array() );
|
||||
|
||||
if ( !isset( $post_types_option[$post_type]['add_meta_box'] ) || $post_types_option[$post_type]['add_meta_box'] ) {
|
||||
|
||||
$output = '<fieldset class="inline-edit-col-right">';
|
||||
$output .= '<div class="bulk-edit-groups">';
|
||||
|
||||
// capability/access restriction bulk actions added through extra_tablenav()
|
||||
$output .= '<div id="capability-bulk-actions" class="capabilities-bulk-container" style="display:inline">';
|
||||
|
||||
$output .= '<label style="display:inline;">';
|
||||
$output .= '<span class="title">';
|
||||
$output .= __( 'Access Restrictions', 'groups' );
|
||||
$output .= '</span>';
|
||||
$output .= '<select class="capabilities-action" name="capabilities-action">';
|
||||
$output .= '<option selected="selected" value="-1">' . __( '— No Change —', 'groups' ) . '</option>';
|
||||
$output .= '<option value="add-capability">' . __( 'Add restriction', 'groups' ) . '</option>';
|
||||
$output .= '<option value="remove-capability">' . __( 'Remove restriction', 'groups' ) . '</option>';
|
||||
$output .= '</select>';
|
||||
$output .= '</label>';
|
||||
|
||||
$output .= '<div class="groups-capabilities-container">';
|
||||
$valid_read_caps = Groups_Access_Meta_Boxes_Legacy::get_valid_read_caps_for_user();
|
||||
$output .= sprintf(
|
||||
'<select class="select bulk-capability" name="%s[]" multiple="multiple" placeholder="%s" data-placeholder="%s">',
|
||||
esc_attr( Groups_Post_Access_Legacy::POSTMETA_PREFIX . 'bulk-' . Groups_Post_Access_Legacy::READ_POST_CAPABILITY ),
|
||||
esc_attr( __( 'Choose access restrictions …', 'groups' ) ) ,
|
||||
esc_attr( __( 'Choose access restrictions …', 'groups' ) )
|
||||
);
|
||||
|
||||
foreach( $valid_read_caps as $capability ) {
|
||||
$output .= sprintf( '<option value="%s" >%s</option>', esc_attr( $capability ), wp_filter_nohtml_kses( $capability ) );
|
||||
}
|
||||
$output .= '</select>';
|
||||
$output .= '</div>'; // .groups-capabilities-container
|
||||
$output .= Groups_UIE::render_select( '.select.bulk-capability' );
|
||||
|
||||
$output .= '</div>'; // .capabilities-bulk-container
|
||||
|
||||
$output .= '</div>'; // .bulk-edit-groups
|
||||
$output .= '</fieldset>'; // .inline-edit-col-right
|
||||
|
||||
$output .= wp_nonce_field( 'post-capability', 'bulk-post-capability-nonce', true, false );
|
||||
|
||||
echo $output;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles access restriction capability modifications from bulk-editing.
|
||||
* This is called once for each post that is included in bulk-editing.
|
||||
* The fields that are handled here are rendered through the
|
||||
* bulk_edit_custom_box() method in this class.
|
||||
*
|
||||
* @param int $post_id
|
||||
*/
|
||||
public static function save_post( $post_id ) {
|
||||
if ( isset( $_REQUEST['capabilities-action'] ) ) {
|
||||
if ( wp_verify_nonce( $_REQUEST['bulk-post-capability-nonce'], 'post-capability' ) ) {
|
||||
$field = Groups_Post_Access_Legacy::POSTMETA_PREFIX . 'bulk-' . Groups_Post_Access_Legacy::READ_POST_CAPABILITY;
|
||||
if ( !empty( $_REQUEST[$field] ) && is_array( $_REQUEST[$field] ) ) {
|
||||
if ( Groups_Access_Meta_Boxes_Legacy::user_can_restrict() ) {
|
||||
$valid_read_caps = Groups_Access_Meta_Boxes_Legacy::get_valid_read_caps_for_user();
|
||||
foreach( $_REQUEST[$field] as $capability_name ) {
|
||||
if ( $capability = Groups_Capability::read_by_capability( $capability_name ) ) {
|
||||
if ( in_array( $capability->capability, $valid_read_caps ) ) {
|
||||
switch( $_REQUEST['capabilities-action'] ) {
|
||||
case 'add-capability' :
|
||||
Groups_Post_Access_Legacy::create( array(
|
||||
'post_id' => $post_id,
|
||||
'capability' => $capability->capability
|
||||
) );
|
||||
break;
|
||||
case 'remove-capability' :
|
||||
Groups_Post_Access_Legacy::delete( $post_id, $capability->capability );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Query modifier to take the selected access restriction capability into
|
||||
* account.
|
||||
*
|
||||
* @param WP_Query $query query object passed by reference
|
||||
*/
|
||||
public static function parse_query( &$query ) {
|
||||
|
||||
global $pagenow;
|
||||
|
||||
if ( is_admin() ) {
|
||||
|
||||
if ( $pagenow == 'edit.php' ) { // check that we're on the right screen
|
||||
|
||||
$post_type = isset( $_GET['post_type'] ) ? $_GET['post_type'] : 'post';
|
||||
$post_types_option = Groups_Options::get_option( Groups_Post_Access_Legacy::POST_TYPES, array() );
|
||||
|
||||
if ( !isset( $post_types_option[$post_type]['add_meta_box'] ) || $post_types_option[$post_type]['add_meta_box'] ) {
|
||||
|
||||
if ( !empty( $_GET[Groups_Post_Access_Legacy::POSTMETA_PREFIX . Groups_Post_Access_Legacy::READ_POST_CAPABILITY] ) &&
|
||||
is_array( $_GET[Groups_Post_Access_Legacy::POSTMETA_PREFIX . Groups_Post_Access_Legacy::READ_POST_CAPABILITY] )
|
||||
) {
|
||||
|
||||
$include_unrestricted = false;
|
||||
if ( in_array( self::NOT_RESTRICTED, $_GET[Groups_Post_Access_Legacy::POSTMETA_PREFIX . Groups_Post_Access_Legacy::READ_POST_CAPABILITY] ) ) {
|
||||
$include_unrestricted = true;
|
||||
}
|
||||
|
||||
$capabilities = array();
|
||||
foreach ( $_GET[Groups_Post_Access_Legacy::POSTMETA_PREFIX . Groups_Post_Access_Legacy::READ_POST_CAPABILITY] as $capability ) {
|
||||
if ( Groups_Capability::read_by_capability( $capability ) ) {
|
||||
$capabilities[] = $capability;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !empty( $capabilities ) ) {
|
||||
if ( $include_unrestricted ) {
|
||||
// meta_query does not handle a conjunction
|
||||
// on the same meta field correctly
|
||||
// (at least not up to WordPress 3.7.1)
|
||||
// $query->query_vars['meta_query'] = array (
|
||||
// 'relation' => 'OR',
|
||||
// array (
|
||||
// 'key' => Groups_Post_Access_Legacy::POSTMETA_PREFIX . Groups_Post_Access_Legacy::READ_POST_CAPABILITY,
|
||||
// 'value' => $capabilities,
|
||||
// 'compare' => 'IN'
|
||||
// ),
|
||||
// array (
|
||||
// 'key' => Groups_Post_Access_Legacy::POSTMETA_PREFIX . Groups_Post_Access_Legacy::READ_POST_CAPABILITY,
|
||||
// 'compare' => 'NOT EXISTS'
|
||||
// )
|
||||
// );
|
||||
// we'll limit it to show just unrestricted entries
|
||||
// until the above is solved
|
||||
$query->query_vars['meta_query'] = array (
|
||||
array (
|
||||
'key' => Groups_Post_Access_Legacy::POSTMETA_PREFIX . Groups_Post_Access_Legacy::READ_POST_CAPABILITY,
|
||||
'compare' => 'NOT EXISTS'
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$query->query_vars['meta_query'] = array (
|
||||
array (
|
||||
'key' => Groups_Post_Access_Legacy::POSTMETA_PREFIX . Groups_Post_Access_Legacy::READ_POST_CAPABILITY,
|
||||
'value' => $capabilities,
|
||||
'compare' => 'IN'
|
||||
)
|
||||
);
|
||||
}
|
||||
} else if ( $include_unrestricted ) {
|
||||
$query->query_vars['meta_query'] = array (
|
||||
array (
|
||||
'key' => Groups_Post_Access_Legacy::POSTMETA_PREFIX . Groups_Post_Access_Legacy::READ_POST_CAPABILITY,
|
||||
'compare' => 'NOT EXISTS'
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Groups_Admin_Posts_Legacy::init();
|
||||
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
/**
|
||||
* groups-admin-options-legacy.php
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 2.0.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Legacy options admin screen extension.
|
||||
* @param $legacy_switched boolean whether legacy mode setting has been changed during submit
|
||||
*/
|
||||
function groups_admin_options_legacy( $legacy_switched ) {
|
||||
|
||||
global $wpdb;
|
||||
|
||||
require_once GROUPS_LEGACY_LIB . '/access/class-groups-post-access-legacy.php';
|
||||
|
||||
//
|
||||
// handle legacy options after form submission
|
||||
//
|
||||
if ( isset( $_POST['submit'] ) && !$legacy_switched ) {
|
||||
if ( wp_verify_nonce( $_POST[GROUPS_ADMIN_OPTIONS_NONCE], 'admin' ) ) {
|
||||
$valid_read_caps = array( Groups_Post_Access_Legacy::READ_POST_CAPABILITY );
|
||||
if ( !empty( $_POST[GROUPS_READ_POST_CAPABILITIES] ) ) {
|
||||
$read_caps = $_POST[GROUPS_READ_POST_CAPABILITIES];
|
||||
foreach( $read_caps as $read_cap ) {
|
||||
if ( $valid_cap = Groups_Capability::read( $read_cap ) ) {
|
||||
if ( !in_array( $valid_cap->capability, $valid_read_caps ) ) {
|
||||
$valid_read_caps[] = $valid_cap->capability;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Groups_Options::update_option( Groups_Post_Access_Legacy::READ_POST_CAPABILITIES, $valid_read_caps );
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// render legacy settings
|
||||
//
|
||||
echo '<h3>' . __( 'Capabilities', 'groups' ) . '</h3>';
|
||||
|
||||
echo '<p class="description">' .
|
||||
__( 'Include these capabilities to enforce read access on posts. The selected capabilities will be offered to restrict access to posts.', 'groups' ) .
|
||||
'</p>';
|
||||
|
||||
$capability_table = _groups_get_tablename( 'capability' );
|
||||
$capabilities = $wpdb->get_results( "SELECT * FROM $capability_table ORDER BY capability" );
|
||||
$applicable_read_caps = Groups_Options::get_option( Groups_Post_Access_Legacy::READ_POST_CAPABILITIES, array( Groups_Post_Access_Legacy::READ_POST_CAPABILITY ) );
|
||||
echo '<div class="select-capability-container" style="width:62%;">';
|
||||
printf( '<select class="select capability" name="%s" multiple="multiple">', GROUPS_READ_POST_CAPABILITIES . '[]' );
|
||||
foreach( $capabilities as $capability ) {
|
||||
$selected = in_array( $capability->capability, $applicable_read_caps ) ? ' selected="selected" ' : '';
|
||||
if ( $capability->capability == Groups_Post_Access_Legacy::READ_POST_CAPABILITY ) {
|
||||
$selected .= ' disabled="disabled" ';
|
||||
}
|
||||
printf( '<option value="%s" %s>%s</option>', esc_attr( $capability->capability_id ), $selected, wp_filter_nohtml_kses( $capability->capability ) );
|
||||
}
|
||||
echo '</select>';
|
||||
echo '</div>'; // .select-capability-container
|
||||
|
||||
echo Groups_UIE::render_select( '.select.capability' );
|
||||
|
||||
}
|
||||
|
||||
add_action( 'groups_admin_options_legacy', 'groups_admin_options_legacy' );
|
||||
@@ -0,0 +1,576 @@
|
||||
<?php
|
||||
/**
|
||||
* class-groups-access-meta-boxes.php
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.0.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds meta boxes to edit screens.
|
||||
*
|
||||
* @link http://codex.wordpress.org/Function_Reference/add_meta_box
|
||||
*/
|
||||
class Groups_Access_Meta_Boxes {
|
||||
|
||||
const CAPABILITY_NONCE = 'groups-meta-box-capability-nonce';
|
||||
const SET_CAPABILITY = 'set-capability';
|
||||
const READ_ACCESS = 'read-access';
|
||||
const CAPABILITY = 'capability';
|
||||
const SHOW_GROUPS = 'access-meta-box-show-groups';
|
||||
|
||||
const NONCE = 'groups-meta-box-nonce';
|
||||
const SET_GROUPS = 'set-groups';
|
||||
const GROUPS_READ = 'groups-read';
|
||||
const READ = 'read';
|
||||
|
||||
/**
|
||||
* Sets up an init hook where actions and filters are added.
|
||||
*/
|
||||
public static function init() {
|
||||
add_action( 'init', array( __CLASS__, 'wp_init' ) );
|
||||
add_action( 'admin_init', array(__CLASS__,'admin_init' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooks for capabilities meta box and saving options.
|
||||
*/
|
||||
public static function wp_init() {
|
||||
if ( current_user_can( GROUPS_ACCESS_GROUPS ) ) {
|
||||
require_once GROUPS_VIEWS_LIB . '/class-groups-uie.php';
|
||||
|
||||
add_action( 'add_meta_boxes', array( __CLASS__, 'add_meta_boxes' ), 10, 2 );
|
||||
add_action( 'save_post', array( __CLASS__, 'save_post' ), 10, 2 );
|
||||
add_filter( 'wp_insert_post_empty_content', array( __CLASS__, 'wp_insert_post_empty_content' ), 10, 2 );
|
||||
|
||||
add_filter( 'attachment_fields_to_edit', array( __CLASS__, 'attachment_fields_to_edit' ), 10, 2 );
|
||||
add_filter( 'attachment_fields_to_save', array( __CLASS__, 'attachment_fields_to_save' ), 10, 2 );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooked on admin_init to register our action on admin_enqueue_scripts.
|
||||
*/
|
||||
public static function admin_init() {
|
||||
add_action( 'admin_enqueue_scripts', array( __CLASS__, 'admin_enqueue_scripts' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooked on admin_enqueue_scripts to timely enqueue resources required
|
||||
* on the media upload / attachment popup.
|
||||
*/
|
||||
public static function admin_enqueue_scripts() {
|
||||
global $pagenow;
|
||||
if ( $pagenow == 'upload.php' ) {
|
||||
Groups_UIE::enqueue( 'select' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggered by init() to add capability meta box.
|
||||
*/
|
||||
public static function add_meta_boxes( $post_type, $post = null ) {
|
||||
global $wp_version;
|
||||
$post_type_object = get_post_type_object( $post_type );
|
||||
if ( $post_type_object && $post_type != 'attachment' ) {
|
||||
$post_types_option = Groups_Options::get_option( Groups_Post_Access::POST_TYPES, array() );
|
||||
if ( !isset( $post_types_option[$post_type]['add_meta_box'] ) || $post_types_option[$post_type]['add_meta_box'] ) {
|
||||
|
||||
add_meta_box(
|
||||
'groups-permissions',
|
||||
_x( 'Groups', 'Meta box title', 'groups' ),
|
||||
array( __CLASS__, 'groups' ),
|
||||
null,
|
||||
'side',
|
||||
'high'
|
||||
);
|
||||
|
||||
Groups_UIE::enqueue( 'select' );
|
||||
|
||||
if ( self::user_can_restrict() ) {
|
||||
if ( $screen = get_current_screen() ) {
|
||||
// help tab for group-based access restrictions
|
||||
$screen->add_help_tab( array(
|
||||
'id' => 'groups-groups',
|
||||
'title' => __( 'Groups', 'groups' ),
|
||||
'content' =>
|
||||
'<p>' .
|
||||
'<strong>' . __( 'Groups', 'groups' ) . '</strong>' .
|
||||
'</p>' .
|
||||
'<p>' .
|
||||
__( 'Use the <em>Groups</em> box to limit the visibility of posts, pages and other post types.', 'groups' ) .
|
||||
'</p>' .
|
||||
'<p>' .
|
||||
__( 'You can select one or more groups to restrict access to its members.', 'groups' ) .
|
||||
( !current_user_can( GROUPS_ADMINISTER_GROUPS ) ?
|
||||
' ' .
|
||||
__( 'Note that you must be a member of a group to use it to restrict access.', 'groups' )
|
||||
:
|
||||
''
|
||||
) .
|
||||
'</p>' .
|
||||
'<p>' .
|
||||
'<strong>' . __( 'Example:', 'groups' ) . '</strong>' .
|
||||
'</p>' .
|
||||
__( 'Let\'s assume that you want to limit the visibility of a post to members of the <em>Premium</em> group.', 'groups' ) .
|
||||
'<p>' .
|
||||
' ' .
|
||||
'</p>' .
|
||||
__( 'Choose or enter <em>Premium</em> in the <em>Read</em> field located in the <em>Groups</em> box and save or update the post (or hit Enter).', 'groups' ) .
|
||||
'<p>' .
|
||||
( current_user_can( GROUPS_ADMINISTER_GROUPS ) ?
|
||||
'<p>' .
|
||||
__( 'In the same field, you can create a new group and restrict access. Group names are case-sensitive. In order to be able to use the new group, your user account will be assigned to it.', 'groups' ) .
|
||||
'</p>'
|
||||
:
|
||||
''
|
||||
)
|
||||
) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render meta box for groups.
|
||||
*
|
||||
* @see do_meta_boxes()
|
||||
*
|
||||
* @param Object $object
|
||||
* @param Object $box
|
||||
*/
|
||||
public static function groups( $object = null, $box = null ) {
|
||||
|
||||
$output = '';
|
||||
|
||||
$post_id = isset( $object->ID ) ? $object->ID : null;
|
||||
$post_type = isset( $object->post_type ) ? $object->post_type : null;
|
||||
$post_singular_name = __( 'Post', 'groups' );
|
||||
if ( $post_type !== null ) {
|
||||
$post_type_object = get_post_type_object( $post_type );
|
||||
$labels = isset( $post_type_object->labels ) ? $post_type_object->labels : null;
|
||||
if ( $labels !== null ) {
|
||||
if ( isset( $labels->singular_name ) ) {
|
||||
$post_singular_name = __( $labels->singular_name );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$output .= wp_nonce_field( self::SET_GROUPS, self::NONCE, true, false );
|
||||
|
||||
$output .= apply_filters( 'groups_access_meta_boxes_groups_before_read_groups', '', $object, $box );
|
||||
|
||||
$output .= '<div class="select-read-groups-container">';
|
||||
|
||||
if ( self::user_can_restrict() ) {
|
||||
|
||||
$include = self::get_user_can_restrict_group_ids();
|
||||
$groups = Groups_Group::get_groups( array( 'order_by' => 'name', 'order' => 'ASC', 'include' => $include ) );
|
||||
$groups_read = get_post_meta( $post_id, Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ );
|
||||
|
||||
$read_help = sprintf(
|
||||
__( 'You can restrict the visibility of this %1$s to group members. Choose one or more groups that are allowed to read this %2$s. If no groups are chosen, the %3$s is visible to anyone.', 'groups' ),
|
||||
$post_singular_name,
|
||||
$post_singular_name,
|
||||
$post_singular_name
|
||||
);
|
||||
if ( current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
|
||||
$read_help .= ' ' . __( 'You can create a new group by indicating the group\'s name.', 'groups' );
|
||||
}
|
||||
|
||||
$output .= sprintf(
|
||||
'<label title="%s">',
|
||||
esc_attr( $read_help )
|
||||
);
|
||||
$output .= __( 'Read', 'groups' );
|
||||
$output .= ' ';
|
||||
|
||||
$output .= sprintf(
|
||||
'<select class="select groups-read" name="%s" multiple="multiple" placeholder="%s" data-placeholder="%s" title="%s">',
|
||||
self::GROUPS_READ . '[]',
|
||||
esc_attr( __( 'Anyone …', 'groups' ) ),
|
||||
esc_attr( __( 'Anyone …', 'groups' ) ),
|
||||
esc_attr( $read_help )
|
||||
);
|
||||
$output .= '<option value=""></option>';
|
||||
foreach( $groups as $group ) {
|
||||
$output .= sprintf( '<option value="%s" %s>', esc_attr( $group->group_id ), in_array( $group->group_id, $groups_read ) ? ' selected="selected" ' : '' );
|
||||
$output .= wp_filter_nohtml_kses( $group->name );
|
||||
$output .= '</option>';
|
||||
}
|
||||
$output .= '</select>';
|
||||
$output .= '</label>';
|
||||
$output .= Groups_UIE::render_select(
|
||||
'.select.groups-read',
|
||||
true,
|
||||
true,
|
||||
current_user_can( GROUPS_ADMINISTER_GROUPS )
|
||||
);
|
||||
$output .= '<p class="description">';
|
||||
$output .= sprintf(
|
||||
__( 'Restricts the visibility of this %s to members of the chosen groups.', 'groups' ),
|
||||
$post_singular_name
|
||||
);
|
||||
$output .= '</p>';
|
||||
|
||||
} else {
|
||||
$output .= '<p class="description">';
|
||||
$output .= sprintf( __( 'You cannot set any access restrictions.', 'groups' ), $post_singular_name );
|
||||
$style = 'cursor:help;vertical-align:middle;';
|
||||
if ( current_user_can( GROUPS_ADMINISTER_OPTIONS ) ) {
|
||||
$style = 'cursor:pointer;vertical-align:middle;';
|
||||
$output .= sprintf( '<a href="%s">', esc_url( admin_url( 'admin.php?page=groups-admin-options' ) ) );
|
||||
}
|
||||
$output .= sprintf( '<img style="%s" alt="?" title="%s" src="%s" />', $style, esc_attr( __( 'You need to have permission to set access restrictions.', 'groups' ) ), esc_attr( GROUPS_PLUGIN_URL . 'images/help.png' ) );
|
||||
if ( current_user_can( GROUPS_ADMINISTER_OPTIONS ) ) {
|
||||
$output .= '</a>';
|
||||
}
|
||||
$output .= '</p>';
|
||||
}
|
||||
|
||||
$output .= '</div>'; // .select-read-groups-container
|
||||
|
||||
$output .= apply_filters( 'groups_access_meta_boxes_groups_after_read_groups', '', $object, $box );
|
||||
|
||||
$output = apply_filters( 'groups_access_meta_boxes_groups', $output, $object, $box );
|
||||
|
||||
echo $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes our save_post() if the post content is considered empty.
|
||||
* This is required because even on an empty post, we want to allow to
|
||||
* quick-create group and category as well as assign capabilities.
|
||||
* At WordPress 3.6.1, this is the only way we can achieve that, because
|
||||
* the save_post action is not invoked if the post content is considered
|
||||
* empty.
|
||||
*
|
||||
* @param boolean $maybe_empty
|
||||
* @param array $postarr
|
||||
* @return boolean
|
||||
*/
|
||||
public static function wp_insert_post_empty_content( $maybe_empty, $postarr ) {
|
||||
|
||||
// Only consider invoking save_post() here, if the post content is
|
||||
// considered to be empty at this stage. This is so we don't end up
|
||||
// having save_post() invoked twice when the post is not empty.
|
||||
if ( $maybe_empty ) {
|
||||
$post_id = !empty( $postarr['ID'] ) ? $postarr['ID'] : !empty( $postarr['post_ID'] ) ? $postarr['post_ID'] : null;
|
||||
if ( $post_id ) {
|
||||
self::save_post( $post_id );
|
||||
}
|
||||
}
|
||||
|
||||
return $maybe_empty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the group access restriction.
|
||||
*
|
||||
* @param int $post_id
|
||||
* @param mixed $post post data (not used here)
|
||||
*/
|
||||
public static function save_post( $post_id = null, $post = null ) {
|
||||
// This is called multiple times and if a new post is created and a new group is requested*,
|
||||
// we can end up without the new group being assigned to the post unless we duely check
|
||||
// for revision and autosave:
|
||||
// (* on the second call, the new group exists and it will bail out on "if ( !( $group = Groups_Group::read_by_name( $name ) ) ) { ...")
|
||||
if ( ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE || wp_is_post_revision( $post_id ) || wp_is_post_autosave( $post_id ) ) ) {
|
||||
} else {
|
||||
$post_type = get_post_type( $post_id );
|
||||
$post_type_object = get_post_type_object( $post_type );
|
||||
if ( $post_type_object && $post_type != 'attachment' ) {
|
||||
$post_types_option = Groups_Options::get_option( Groups_Post_Access::POST_TYPES, array() );
|
||||
if ( !isset( $post_types_option[$post_type]['add_meta_box'] ) || $post_types_option[$post_type]['add_meta_box'] ) {
|
||||
|
||||
if ( self::user_can_restrict() ) {
|
||||
if ( isset( $_POST[self::NONCE] ) && wp_verify_nonce( $_POST[self::NONCE], self::SET_GROUPS ) ) {
|
||||
$post_type = isset( $_POST['post_type'] ) ? $_POST['post_type'] : null;
|
||||
if ( $post_type !== null ) {
|
||||
|
||||
// See http://codex.wordpress.org/Function_Reference/current_user_can 20130119 WP 3.5
|
||||
// "... Some capability checks (like 'edit_post' or 'delete_page') require this [the post ID] be provided."
|
||||
// If the post ID is not provided, it will throw:
|
||||
// PHP Notice: Undefined offset: 0 in /var/www/groups-forums/wp-includes/capabilities.php on line 1067
|
||||
$edit_post_type = 'edit_' . $post_type;
|
||||
if ( $post_type_object = get_post_type_object( $post_type ) ) {
|
||||
if ( !isset( $post_type_object->capabilities ) ) {
|
||||
// get_post_type_capabilities() (WP 3.8) will throw a warning
|
||||
// when trying to merge the missing property otherwise. It's either a
|
||||
// bug or the function's documentation should make it clear that you
|
||||
// have to provide that.
|
||||
$post_type_object->capabilities = array();
|
||||
}
|
||||
$caps_object = get_post_type_capabilities( $post_type_object );
|
||||
if ( isset( $caps_object->edit_post ) ) {
|
||||
$edit_post_type = $caps_object->edit_post;
|
||||
}
|
||||
}
|
||||
|
||||
if ( current_user_can( $edit_post_type, $post_id ) ) {
|
||||
$include = self::get_user_can_restrict_group_ids();
|
||||
$groups = Groups_Group::get_groups( array( 'order_by' => 'name', 'order' => 'ASC', 'include' => $include ) );
|
||||
$user_group_ids_deep = array();
|
||||
foreach( $groups as $group ) {
|
||||
$user_group_ids_deep[] = $group->group_id;
|
||||
}
|
||||
$group_ids = array();
|
||||
$submitted_group_ids = !empty( $_POST[self::GROUPS_READ] ) && is_array( $_POST[self::GROUPS_READ] ) ? $_POST[self::GROUPS_READ] : array();
|
||||
|
||||
// assign requested groups and create and assign new groups if allowed
|
||||
foreach( $submitted_group_ids as $group_id ) {
|
||||
if ( is_numeric( $group_id ) ) {
|
||||
if ( in_array( $group_id, $user_group_ids_deep ) ) {
|
||||
$group_ids[] = $group_id;
|
||||
}
|
||||
} else {
|
||||
if ( current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
|
||||
$creator_id = get_current_user_id();
|
||||
$datetime = date( 'Y-m-d H:i:s', time() );
|
||||
$name = ucwords( strtolower( trim( preg_replace( '/\s+/', ' ', $group_id ) ) ) );
|
||||
if ( strlen( $name ) > 0 ) {
|
||||
if ( !( $group = Groups_Group::read_by_name( $name ) ) ) {
|
||||
if ( $group_id = Groups_Group::create( compact( 'creator_id', 'datetime', 'name' ) ) ) {
|
||||
if ( Groups_User_Group::create( array( 'user_id' => $creator_id, 'group_id' => $group_id ) ) ) {
|
||||
$group_ids[] = $group_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
do_action( 'groups_access_meta_boxes_before_groups_read_update', $post_id, $group_ids );
|
||||
$update_result = Groups_Post_Access::update( array( 'post_id' => $post_id, 'groups_read' => $group_ids ) );
|
||||
do_action( 'groups_access_meta_boxes_after_groups_read_update', $post_id, $group_ids, $update_result );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue scripts and styles.
|
||||
*/
|
||||
private static function enqueue() {
|
||||
global $groups_version;
|
||||
if ( !wp_script_is( 'selectize' ) ) {
|
||||
wp_enqueue_script( 'selectize', GROUPS_PLUGIN_URL . 'js/selectize/selectize.min.js', array( 'jquery' ), $groups_version, false );
|
||||
}
|
||||
if ( !wp_style_is( 'selectize' ) ) {
|
||||
wp_enqueue_style( 'selectize', GROUPS_PLUGIN_URL . 'css/selectize/selectize.bootstrap2.css', array(), $groups_version );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render groups box for attachment post type (Media).
|
||||
*
|
||||
* @param array $form_fields
|
||||
* @param object $post
|
||||
* @return array
|
||||
*/
|
||||
public static function attachment_fields_to_edit( $form_fields, $post ) {
|
||||
|
||||
Groups_UIE::enqueue( 'select' );
|
||||
|
||||
$post_types_option = Groups_Options::get_option( Groups_Post_Access::POST_TYPES, array() );
|
||||
|
||||
if ( !isset( $post_types_option['attachment']['add_meta_box'] ) || $post_types_option['attachment']['add_meta_box'] ) {
|
||||
|
||||
if ( self::user_can_restrict() ) {
|
||||
|
||||
$include = self::get_user_can_restrict_group_ids();
|
||||
$groups = Groups_Group::get_groups( array( 'order_by' => 'name', 'order' => 'ASC', 'include' => $include ) );
|
||||
$groups_read = get_post_meta( $post->ID, Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ );
|
||||
|
||||
$output = '';
|
||||
$post_singular_name = __( 'Media', 'groups' );
|
||||
|
||||
$output .= __( 'Read', 'groups' );
|
||||
|
||||
// On attachments edited within the 'Insert Media' popup, the update is triggered too soon and we end up with only the last capability selected.
|
||||
// This occurs when using normal checkboxes as well as the select below (Chosen and Selectize tested).
|
||||
// With checkboxes it's even more confusing, it's actually better to have it using a select as below,
|
||||
// because the visual feedback corresponds with what is assigned.
|
||||
// See http://wordpress.org/support/topic/multiple-access-restrictions-for-media-items-are-not-saved-in-grid-view
|
||||
// and https://core.trac.wordpress.org/ticket/28053 - this is an issue with multiple value fields and should
|
||||
// be fixed within WordPress.
|
||||
|
||||
// $output .= '<div style="padding:0 1em;margin:1em 0;border:1px solid #ccc;border-radius:4px;">';
|
||||
// $output .= '<ul>';
|
||||
// foreach( $groups as $group ) {
|
||||
// $checked = in_array( $group->group_id, $groups_read ) ? ' checked="checked" ' : '';
|
||||
// $output .= '<li>';
|
||||
// $output .= '<label>';
|
||||
// $output .= '<input name="attachments[' . $post->ID . '][' . self::GROUPS_READ . '][]" ' . $checked . ' type="checkbox" value="' . esc_attr( $group->group_id ) . '" />';
|
||||
// $output .= wp_filter_nohtml_kses( $group->name );
|
||||
// $output .= '</label>';
|
||||
// $output .= '</li>';
|
||||
// }
|
||||
// $output .= '</ul>';
|
||||
// $output .= '</div>';
|
||||
|
||||
$output .= '<div class="select-groups-container">';
|
||||
$select_id = 'attachments-' . $post->ID . '-' . self::GROUPS_READ;
|
||||
$output .= sprintf(
|
||||
'<select id="%s" class="select groups-read" name="%s" multiple="multiple" placeholder="%s" data-placeholder="%s" title="%s">',
|
||||
$select_id,
|
||||
'attachments[' . $post->ID . '][' . self::GROUPS_READ . '][]',
|
||||
esc_attr( __( 'Anyone …', 'groups' ) ),
|
||||
esc_attr( __( 'Anyone …', 'groups' ) ),
|
||||
__( 'You can restrict the visibility to group members. Choose one or more groups to restrict access. If no groups are chosen, this entry is visible to anyone.', 'groups' ) .
|
||||
current_user_can( GROUPS_ADMINISTER_GROUPS ) ? ' ' . __( 'You can create a new group by indicating the group\'s name.', 'groups' ) : ''
|
||||
);
|
||||
$output .= '<option value=""></option>';
|
||||
foreach( $groups as $group ) {
|
||||
$output .= sprintf( '<option value="%s" %s>', esc_attr( $group->group_id ), in_array( $group->group_id, $groups_read ) ? ' selected="selected" ' : '' );
|
||||
$output .= wp_filter_nohtml_kses( $group->name );
|
||||
$output .= '</option>';
|
||||
}
|
||||
$output .= '</select>';
|
||||
|
||||
$output .= Groups_UIE::render_select( '#'.$select_id, true, true, current_user_can( GROUPS_ADMINISTER_GROUPS ) );
|
||||
|
||||
$output .= '</div>';
|
||||
|
||||
$output .= '<p class="description">';
|
||||
$output .= __( 'Restricts the visibility of this entry to members of the chosen groups.', 'groups' );
|
||||
$output .= '</p>';
|
||||
|
||||
$output .= '<p class="description">';
|
||||
$output .= __( 'The attachment page is restricted to authorized users, but due to technical limitations, the file can still be accessed directly via its URL.', 'groups' );
|
||||
$output .= ' ';
|
||||
$output .= sprintf( __( 'Please use <a href="%s" target="_blank">Groups File Access</a> for files that require complete protection.', 'groups' ), esc_url( 'http://www.itthinx.com/shop/groups-file-access/' ) );
|
||||
$output .= '</p>';
|
||||
|
||||
$form_fields['groups_read'] = array(
|
||||
'label' => __( 'Groups', 'groups' ),
|
||||
'input' => 'html',
|
||||
'html' => $output
|
||||
);
|
||||
}
|
||||
}
|
||||
return $form_fields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save groups for attachment post type (Media).
|
||||
* When multiple attachments are saved, this is called once for each.
|
||||
*
|
||||
* @param array $post post data
|
||||
* @param array $attachment attachment field data
|
||||
* @return array
|
||||
*/
|
||||
public static function attachment_fields_to_save( $post, $attachment ) {
|
||||
$post_types_option = Groups_Options::get_option( Groups_Post_Access::POST_TYPES, array() );
|
||||
if ( !isset( $post_types_option['attachment']['add_meta_box'] ) || $post_types_option['attachment']['add_meta_box'] ) {
|
||||
// if we're here, we assume the user is allowed to edit attachments,
|
||||
// but we still need to check if the user can restrict access
|
||||
if ( self::user_can_restrict() ) {
|
||||
$post_id = null;
|
||||
if ( isset( $post['ID'] ) ) {
|
||||
$post_id = $post['ID'];
|
||||
} else if ( isset( $post['post_ID'] ) ) {
|
||||
$post_id = $post['post_ID'];
|
||||
}
|
||||
if ( $post_id !== null ) {
|
||||
$include = self::get_user_can_restrict_group_ids();
|
||||
$groups = Groups_Group::get_groups( array( 'order_by' => 'name', 'order' => 'ASC', 'include' => $include ) );
|
||||
$group_ids = array();
|
||||
if ( !empty( $attachment[self::GROUPS_READ] ) && is_array( $attachment[self::GROUPS_READ] ) ) {
|
||||
foreach( $groups as $group ) {
|
||||
if ( in_array( $group->group_id, $attachment[self::GROUPS_READ] ) ) {
|
||||
$group_ids[] = $group->group_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
do_action( 'groups_access_meta_boxes_before_groups_read_update', $post_id, $group_ids );
|
||||
$update_result = Groups_Post_Access::update( array( 'post_id' => $post_id, 'groups_read' => $group_ids ) );
|
||||
do_action( 'groups_access_meta_boxes_after_groups_read_update', $post_id, $group_ids, $update_result );
|
||||
}
|
||||
}
|
||||
}
|
||||
return $post;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the user can restrict access to posts. The current user is
|
||||
* assumed by default unless a user ID is provided.
|
||||
*
|
||||
* @param int $user_id indicates the desired user, otherwise for the current user
|
||||
* @return boolean
|
||||
*/
|
||||
public static function user_can_restrict( $user_id = null ) {
|
||||
if ( $user_id === null ) {
|
||||
$user_id = get_current_user_id();
|
||||
}
|
||||
$user = new Groups_User( $user_id);
|
||||
return $user->can( GROUPS_RESTRICT_ACCESS );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the group IDs of the groups that the user can use to restrict access.
|
||||
*
|
||||
* If the user can GROUPS_RESTRICT_ACCESS, the following group IDs are returned:
|
||||
* - If the user can GROUPS_ADMINISTER_GROUPS, this will return the IDs of all groups.
|
||||
* - Otherwise it will return the IDs of all groups that the user belongs to, directly
|
||||
* or indirectly by group inheritance.
|
||||
*
|
||||
* If the user can not GROUPS_RESTRICT_ACCESS, an empty array is returned.
|
||||
*
|
||||
* @param int $user_id if provided, retrieve results for the user indicated by user ID, otherwise for the current user
|
||||
* @return array of int with the group IDs
|
||||
*/
|
||||
public static function get_user_can_restrict_group_ids( $user_id = null ) {
|
||||
$group_ids = array();
|
||||
if ( $user_id === null ) {
|
||||
$user_id = get_current_user_id();
|
||||
}
|
||||
if ( self::user_can_restrict( $user_id ) ) {
|
||||
if ( current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
|
||||
$group_ids = Groups_Group::get_group_ids();
|
||||
} else {
|
||||
$user = new Groups_User( $user_id );
|
||||
$group_ids = $user->group_ids_deep;
|
||||
}
|
||||
if ( !empty( $group_ids ) && is_array( $group_ids ) ) {
|
||||
$group_ids = array_map (array( 'Groups_Utility','id'), $group_ids );
|
||||
}
|
||||
}
|
||||
return $group_ids;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @return array of valid read capabilities for the current or given user
|
||||
*/
|
||||
public static function get_valid_read_caps_for_user( $user_id = null ) {
|
||||
require_once( GROUPS_LEGACY_LIB . '/access/class-groups-access-meta-boxes-legacy.php' );
|
||||
return Groups_Access_Meta_Boxes_Legacy::get_valid_read_caps_for_user( $user_id );
|
||||
}
|
||||
}
|
||||
Groups_Access_Meta_Boxes::init();
|
||||
@@ -0,0 +1,185 @@
|
||||
<?php
|
||||
/**
|
||||
* class-groups-access-shortcodes.php
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.0.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcode handlers.
|
||||
*/
|
||||
class Groups_Access_Shortcodes {
|
||||
|
||||
/**
|
||||
* Defines access shortcodes.
|
||||
*/
|
||||
public static function init() {
|
||||
|
||||
// group restrictions
|
||||
add_shortcode( 'groups_member', array( __CLASS__, 'groups_member' ) );
|
||||
add_shortcode( 'groups_non_member', array( __CLASS__, 'groups_non_member' ) );
|
||||
|
||||
// capabilities
|
||||
add_shortcode( 'groups_can', array( __CLASS__, 'groups_can' ) );
|
||||
add_shortcode( 'groups_can_not', array( __CLASS__, 'groups_can_not' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes one attribute "group" which is a comma-separated list of group
|
||||
* names or ids (can be mixed).
|
||||
* The content is shown if the current user belongs to the group(s).
|
||||
*
|
||||
* @param array $atts attributes
|
||||
* @param string $content content to render
|
||||
*/
|
||||
public static function groups_member( $atts, $content = null ) {
|
||||
$output = '';
|
||||
$options = shortcode_atts( array( 'group' => '' ), $atts );
|
||||
$show_content = false;
|
||||
if ( $content !== null ) {
|
||||
$groups_user = new Groups_User( get_current_user_id() );
|
||||
$groups = explode( ',', $options['group'] );
|
||||
foreach ( $groups as $group ) {
|
||||
$group = trim( $group );
|
||||
$current_group = Groups_Group::read( $group );
|
||||
if ( !$current_group ) {
|
||||
$current_group = Groups_Group::read_by_name( $group );
|
||||
}
|
||||
if ( $current_group ) {
|
||||
if ( Groups_User_Group::read( $groups_user->user->ID , $current_group->group_id ) ) {
|
||||
$show_content = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( $show_content ) {
|
||||
remove_shortcode( 'groups_member' );
|
||||
$content = do_shortcode( $content );
|
||||
add_shortcode( 'groups_member', array( __CLASS__, 'groups_member' ) );
|
||||
$output = $content;
|
||||
}
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes one attribute "group" which is a comma-separated list of group
|
||||
* names or ids (can be mixed).
|
||||
* The content is shown if the current user does NOT belong to the group(s).
|
||||
*
|
||||
* @param array $atts attributes
|
||||
* @param string $content content to render
|
||||
*/
|
||||
public static function groups_non_member( $atts, $content = null ) {
|
||||
$output = '';
|
||||
$options = shortcode_atts( array( 'group' => '' ), $atts );
|
||||
$show_content = true;
|
||||
if ( $content !== null ) {
|
||||
$groups_user = new Groups_User( get_current_user_id() );
|
||||
$groups = explode( ',', $options['group'] );
|
||||
foreach ( $groups as $group ) {
|
||||
$group = trim( $group );
|
||||
$current_group = Groups_Group::read( $group );
|
||||
if ( !$current_group ) {
|
||||
$current_group = Groups_Group::read_by_name( $group );
|
||||
}
|
||||
if ( $current_group ) {
|
||||
if ( Groups_User_Group::read( $groups_user->user->ID , $current_group->group_id ) ) {
|
||||
$show_content = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( $show_content ) {
|
||||
remove_shortcode( 'groups_non_member' );
|
||||
$content = do_shortcode( $content );
|
||||
add_shortcode( 'groups_non_member', array( __CLASS__, 'groups_non_member' ) );
|
||||
$output = $content;
|
||||
}
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes one attribute "capability" that must be a valid capability label
|
||||
* or a list of capabilities separated by comma.
|
||||
* The content is shown if the current user has one of the capabilities.
|
||||
*
|
||||
* @param array $atts attributes
|
||||
* @param string $content content to render
|
||||
*/
|
||||
public static function groups_can( $atts, $content = null ) {
|
||||
$output = '';
|
||||
$options = shortcode_atts( array( 'capability' => '' ), $atts );
|
||||
if ( $content !== null ) {
|
||||
$groups_user = new Groups_User( get_current_user_id() );
|
||||
$capability = $options['capability'];
|
||||
$capabilities = array_map( 'trim', explode( ',', $capability ) );
|
||||
$show_content = false;
|
||||
foreach( $capabilities as $capability ) {
|
||||
if ( $groups_user->can( $capability ) ) {
|
||||
$show_content = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( $show_content ) {
|
||||
remove_shortcode( 'groups_can' );
|
||||
$content = do_shortcode( $content );
|
||||
add_shortcode( 'groups_can', array( __CLASS__, 'groups_can' ) );
|
||||
$output = $content;
|
||||
}
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Takes one attribute "capability" that must be a valid capability label,
|
||||
* or a comma-separaed list of those.
|
||||
* The content is shown if the current user has none of the capabilities.
|
||||
*
|
||||
* @param array $atts attributes
|
||||
* @param string $content content to render
|
||||
*/
|
||||
public static function groups_can_not( $atts, $content = null ) {
|
||||
$output = '';
|
||||
$options = shortcode_atts( array( 'capability' => '' ), $atts );
|
||||
if ( $content !== null ) {
|
||||
$groups_user = new Groups_User( get_current_user_id() );
|
||||
$capability = $options['capability'];
|
||||
$capabilities = array_map( 'trim', explode( ',', $capability ) );
|
||||
$show_content = true;
|
||||
foreach( $capabilities as $capability ) {
|
||||
if ( $groups_user->can( $capability ) ) {
|
||||
$show_content = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( $show_content ) {
|
||||
remove_shortcode( 'groups_can_not' );
|
||||
$content = do_shortcode( $content );
|
||||
add_shortcode( 'groups_can_not', array( __CLASS__, 'groups_can_not' ) );
|
||||
$output = $content;
|
||||
}
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
Groups_Access_Shortcodes::init();
|
||||
@@ -0,0 +1,320 @@
|
||||
<?php
|
||||
/**
|
||||
* class-groups-comment-access.php
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 2.2.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Post access restrictions.
|
||||
*/
|
||||
class Groups_Comment_Access {
|
||||
|
||||
const CACHE_GROUP = 'groups';
|
||||
const COMMENT_COUNTS = 'comment_counts';
|
||||
|
||||
public static function init() {
|
||||
add_filter( 'comments_array', array( __CLASS__, 'comments_array' ), 10, 2 );
|
||||
add_filter( 'comment_feed_where', array( __CLASS__, 'comment_feed_where' ), 10, 2 );
|
||||
add_filter( 'comments_clauses', array( __CLASS__, 'comments_clauses' ), 10, 2 );
|
||||
// the comments_clauses filter is used in WP_Comment_Query::get_comment_ids() before the
|
||||
// comments are filtered with the_comments in WP_Comment_Query::get_comments() so we don't need to do this again
|
||||
//add_filter( 'the_comments', array( __CLASS__, 'the_comments' ), 10, 2 );
|
||||
add_filter( 'wp_count_comments', array( __CLASS__, 'wp_count_comments' ), 999, 2 ); // see wp-includes/comment.php function wp_count_comments(...)
|
||||
add_filter( 'get_comments_number', array( __CLASS__, 'get_comments_number' ), 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter comments on the post if the user can't read the post.
|
||||
*
|
||||
* @param array $comments
|
||||
* @param int $post_id
|
||||
*/
|
||||
public static function comments_array( $comments, $post_id ) {
|
||||
|
||||
if ( !apply_filters( 'groups_comment_access_comments_array_apply', true, $comments, $post_id ) ) {
|
||||
return $comments;
|
||||
}
|
||||
|
||||
$result = array();
|
||||
if ( Groups_Post_Access::user_can_read_post( $post_id ) ) {
|
||||
$result = $comments;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove comments on posts that the user cannot read.
|
||||
*
|
||||
* @param array $comments
|
||||
* @param WP_Comment_Query $comment_query
|
||||
* @return array
|
||||
*/
|
||||
public static function the_comments( $comments, $comment_query ) {
|
||||
|
||||
if ( !apply_filters( 'groups_comment_access_the_comments_apply', true, $comments, $comment_query ) ) {
|
||||
return $comments;
|
||||
}
|
||||
|
||||
$_comments = array();
|
||||
foreach( $comments as $comment ) {
|
||||
if ( isset( $comment->comment_post_ID ) ) {
|
||||
if ( Groups_Post_Access::user_can_read_post( $comment->comment_post_ID ) ) {
|
||||
$_comments[] = $comment;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $_comments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter feed comments.
|
||||
*
|
||||
* @param string $where
|
||||
* @param WP_Query $query
|
||||
* @return string
|
||||
*/
|
||||
public static function comment_feed_where( $where, $query ) {
|
||||
|
||||
if ( !apply_filters( 'groups_comment_access_comment_feed_where_apply', true, $where, $query ) ) {
|
||||
return $where;
|
||||
}
|
||||
|
||||
if ( _groups_admin_override() ) {
|
||||
return $where;
|
||||
}
|
||||
|
||||
if ( current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
|
||||
return $where;
|
||||
}
|
||||
|
||||
$where = self::build_where( $where );
|
||||
return $where;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter the comments based on post read access restrictions.
|
||||
*
|
||||
* @param array $pieces
|
||||
* @param WP_Comment_Query $comment_query
|
||||
* @return array
|
||||
*/
|
||||
public static function comments_clauses( $pieces, $comment_query ) {
|
||||
|
||||
if ( !apply_filters( 'groups_comment_access_comments_clauses_apply', true, $pieces, $comment_query ) ) {
|
||||
return $pieces;
|
||||
}
|
||||
|
||||
if ( _groups_admin_override() ) {
|
||||
return $pieces;
|
||||
}
|
||||
|
||||
if ( current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
|
||||
return $pieces;
|
||||
}
|
||||
|
||||
$where = isset( $pieces['where'] ) ? $pieces['where'] : '';
|
||||
$where = self::build_where( $where );
|
||||
$pieces['where'] = $where;
|
||||
return $pieces;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds conditions to $where to restrict comment access.
|
||||
*
|
||||
* @param unknown $where
|
||||
* @return unknown|string
|
||||
*/
|
||||
private static function build_where( $where ) {
|
||||
|
||||
global $wpdb;
|
||||
|
||||
$handles_post_types = Groups_Post_Access::get_handles_post_types();
|
||||
$post_types = array();
|
||||
foreach( $handles_post_types as $post_type => $handles ) {
|
||||
if ( $handles ) {
|
||||
$post_types[] = $post_type;
|
||||
}
|
||||
}
|
||||
if ( count( $post_types ) == 0 ) {
|
||||
return $where;
|
||||
}
|
||||
$post_types_in = "'" . implode( "','", array_map( 'esc_sql', $post_types ) ) . "'";
|
||||
|
||||
// group_ids : all the groups that the user belongs to, including those that are inherited
|
||||
$user_id = get_current_user_id();
|
||||
$group_ids = array();
|
||||
if ( $user = new Groups_User( $user_id ) ) {
|
||||
$group_ids_deep = $user->group_ids_deep;
|
||||
if ( is_array( $group_ids_deep ) ) {
|
||||
$group_ids = $group_ids_deep;
|
||||
}
|
||||
}
|
||||
|
||||
if ( count( $group_ids ) > 0 ) {
|
||||
$group_ids = "'" . implode( "','", array_map( 'esc_sql', $group_ids ) ) . "'";
|
||||
} else {
|
||||
$group_ids = '\'\'';
|
||||
}
|
||||
|
||||
// only comments from posts that the user can read
|
||||
$where .= sprintf(
|
||||
" AND {$wpdb->comments}.comment_post_ID NOT IN ( " .
|
||||
"SELECT ID FROM $wpdb->posts WHERE " .
|
||||
"post_type IN (%s) AND " .
|
||||
"ID IN ( " .
|
||||
"SELECT post_id FROM $wpdb->postmeta pm WHERE " .
|
||||
"pm.meta_key = '%s' AND pm.meta_value NOT IN (%s) AND " .
|
||||
"post_id NOT IN ( SELECT post_id FROM $wpdb->postmeta pm WHERE pm.meta_key = '%s' AND pm.meta_value IN (%s) ) " .
|
||||
") " .
|
||||
") ",
|
||||
$post_types_in,
|
||||
esc_sql( Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ ),
|
||||
$group_ids,
|
||||
esc_sql( Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ ),
|
||||
$group_ids
|
||||
);
|
||||
|
||||
return $where;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters comment counts.
|
||||
*
|
||||
* @param array $count
|
||||
* @param int $post_id
|
||||
* @return object comment counts as properties of the object
|
||||
*/
|
||||
public static function wp_count_comments( $count, $post_id ) {
|
||||
|
||||
if ( !apply_filters( 'groups_comment_access_wp_count_comments_apply', true, $count, $post_id ) ) {
|
||||
return $count;
|
||||
}
|
||||
|
||||
if ( _groups_admin_override() ) {
|
||||
return $count;
|
||||
}
|
||||
|
||||
if ( current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
|
||||
return $count;
|
||||
}
|
||||
|
||||
$user_id = get_current_user_id();
|
||||
$cached = Groups_Cache::get( self::COMMENT_COUNTS . '_' . $user_id . '_' . intval( $post_id ), self::CACHE_GROUP );
|
||||
if ( $cached !== null ) {
|
||||
$count = $cached->value;
|
||||
unset( $cached );
|
||||
} else {
|
||||
$count = self::get_comment_count( $post_id );
|
||||
Groups_Cache::set( self::COMMENT_COUNTS . '_' . $user_id . '_' . intval( $post_id ), $count, self::CACHE_GROUP );
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters the comments number of a post.
|
||||
*
|
||||
* @param int $count
|
||||
* @param int $post_id
|
||||
* @return int number of comments (0 if there are none or the user can't read the post)
|
||||
*/
|
||||
public static function get_comments_number( $count, $post_id ) {
|
||||
$num_comments = 0;
|
||||
if ( Groups_Post_Access::user_can_read_post( $post_id ) ) {
|
||||
$num_comments = $count;
|
||||
}
|
||||
return $num_comments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapated from get_comment_count() to user our filter.
|
||||
*
|
||||
* @param number $post_id
|
||||
* @return object comment counts as properties of the returned object
|
||||
*/
|
||||
private static function get_comment_count( $post_id = 0 ) {
|
||||
global $wpdb;
|
||||
|
||||
$post_id = (int) $post_id;
|
||||
|
||||
$where = '';
|
||||
if ( $post_id > 0 ) {
|
||||
$where = $wpdb->prepare( "WHERE comment_post_ID = %d ", $post_id );
|
||||
} else {
|
||||
$where = 'WHERE 1=1 ';
|
||||
}
|
||||
|
||||
$where = self::build_where( $where );
|
||||
|
||||
$where = apply_filters( 'groups_comment_access_comment_count_where', $where, $post_id );
|
||||
|
||||
$totals = (array) $wpdb->get_results(
|
||||
"SELECT comment_approved, COUNT( * ) AS total " .
|
||||
"FROM {$wpdb->comments} " .
|
||||
"{$where} " .
|
||||
"GROUP BY comment_approved ",
|
||||
ARRAY_A
|
||||
);
|
||||
|
||||
$comment_count = array(
|
||||
'approved' => 0,
|
||||
'awaiting_moderation' => 0,
|
||||
'spam' => 0,
|
||||
'trash' => 0,
|
||||
'post-trashed' => 0,
|
||||
'total_comments' => 0,
|
||||
'all' => 0,
|
||||
);
|
||||
|
||||
foreach ( $totals as $row ) {
|
||||
switch ( $row['comment_approved'] ) {
|
||||
case 'trash':
|
||||
$comment_count['trash'] = $row['total'];
|
||||
break;
|
||||
case 'post-trashed':
|
||||
$comment_count['post-trashed'] = $row['total'];
|
||||
break;
|
||||
case 'spam':
|
||||
$comment_count['spam'] = $row['total'];
|
||||
$comment_count['total_comments'] += $row['total'];
|
||||
break;
|
||||
case '1':
|
||||
$comment_count['approved'] = $row['total'];
|
||||
$comment_count['total_comments'] += $row['total'];
|
||||
$comment_count['all'] += $row['total'];
|
||||
break;
|
||||
case '0':
|
||||
$comment_count['awaiting_moderation'] = $row['total'];
|
||||
$comment_count['total_comments'] += $row['total'];
|
||||
$comment_count['all'] += $row['total'];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
$comment_count['moderated'] = $comment_count['awaiting_moderation'];
|
||||
//unset( $stats['awaiting_moderation'] );
|
||||
return (object) $comment_count;
|
||||
}
|
||||
}
|
||||
Groups_Comment_Access::init();
|
||||
@@ -0,0 +1,838 @@
|
||||
<?php
|
||||
/**
|
||||
* class-groups-post-access.php
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.0.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Post access restrictions.
|
||||
*/
|
||||
class Groups_Post_Access {
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
const POSTMETA_PREFIX = 'groups-';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
const READ = 'read';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
const CACHE_GROUP = 'groups';
|
||||
|
||||
/**
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const CAN_READ_POST = 'can_read_post';
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @var string
|
||||
*/
|
||||
const READ_POST_CAPABILITY = 'groups_read_post';
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @var string
|
||||
*/
|
||||
const READ_POST_CAPABILITY_NAME = 'Read Post';
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
* @var string
|
||||
*/
|
||||
const READ_POST_CAPABILITIES = 'read_post_capabilities';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
const POST_TYPES = 'post_types';
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
const COUNT_POSTS = 'count-posts';
|
||||
|
||||
/**
|
||||
* Work done on activation, currently does nothing.
|
||||
* @see Groups_Controller::activate()
|
||||
*/
|
||||
public static function activate() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up filters to restrict access.
|
||||
*/
|
||||
public static function init() {
|
||||
// post access
|
||||
add_filter( 'posts_where', array( __CLASS__, 'posts_where' ), 10, 2 );
|
||||
add_filter( 'get_pages', array( __CLASS__, 'get_pages' ), 1 );
|
||||
if ( apply_filters( 'groups_filter_the_posts', false ) ) {
|
||||
add_filter( 'the_posts', array( __CLASS__, 'the_posts' ), 1, 2 );
|
||||
}
|
||||
// If we had a get_post filter https://core.trac.wordpress.org/ticket/12955
|
||||
// add_filter( 'get_post', ... );
|
||||
add_filter( 'wp_get_nav_menu_items', array( __CLASS__, 'wp_get_nav_menu_items' ), 1, 3 );
|
||||
// content access
|
||||
add_filter( 'get_the_excerpt', array( __CLASS__, 'get_the_excerpt' ), 1 );
|
||||
add_filter( 'the_content', array( __CLASS__, 'the_content' ), 1 );
|
||||
// edit & delete post
|
||||
add_filter( 'map_meta_cap', array( __CLASS__, 'map_meta_cap' ), 10, 4 );
|
||||
// @todo these could be interesting to add later ...
|
||||
// add_filter( "plugin_row_meta", array( __CLASS__, "plugin_row_meta" ), 1 );
|
||||
// add_filter( "posts_join_paged", array( __CLASS__, "posts_join_paged" ), 1 );
|
||||
// add_filter( "posts_where_paged", array( __CLASS__, "posts_where_paged" ), 1 );
|
||||
|
||||
add_action( 'groups_deleted_group', array( __CLASS__, 'groups_deleted_group' ) );
|
||||
add_filter( 'wp_count_posts', array( __CLASS__, 'wp_count_posts' ), 10, 3 );
|
||||
// @todo enable the filter and implement below if needed to correct attachment counts
|
||||
// add_filter( 'wp_count_attachments', array( __CLASS__, 'wp_count_attachments' ), 10, 2 );
|
||||
|
||||
// REST API
|
||||
$post_types = self::get_handles_post_types();
|
||||
if ( !empty( $post_types ) ) {
|
||||
foreach( $post_types as $post_type => $handles ) {
|
||||
if ( $handles ) {
|
||||
add_filter( "rest_prepare_{$post_type}", array( __CLASS__, 'rest_prepare_post' ), 10, 3 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// adjacent posts
|
||||
add_filter( 'get_previous_post_where', array( __CLASS__, 'get_previous_post_where' ), 10, 5 );
|
||||
add_filter( 'get_next_post_where', array( __CLASS__, 'get_next_post_where' ), 10, 5 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Replicates the response for invalid post IDs when unauthorized access to a post is requested.
|
||||
* There is no filter in WP_REST_Posts_Controller::get_post() nor in get_post() that we could use (WP 4.8).
|
||||
*
|
||||
* REST API Handbook https://developer.wordpress.org/rest-api/
|
||||
*
|
||||
* For development tests:
|
||||
*
|
||||
* 1. Install https://github.com/WP-API/Basic-Auth
|
||||
* 2. Protect post 1 with group "Test".
|
||||
* 3. Test access denied: $ curl http://example.com/wp-json/wp/v2/posts/1
|
||||
* 4. Test access granted $ curl --user username:password https://example.com/wp-json/wp/v2/posts/1
|
||||
*
|
||||
* On #4 username:password are cleartext, username must belong to group "Test".
|
||||
*
|
||||
* @param array $response
|
||||
* @param WP_Post $post
|
||||
* @param string $request
|
||||
* @return string[]|number[][]
|
||||
*/
|
||||
public static function rest_prepare_post( $response, $post, $request ) {
|
||||
if ( isset( $post->ID ) && !self::user_can_read_post( $post->ID ) ) {
|
||||
$response = array(
|
||||
'code' => 'rest_post_invalid_id',
|
||||
'message' => __( 'Invalid post ID.' ),
|
||||
'data' => array( 'status' => 404 )
|
||||
);
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Restrict access to edit or delete posts based on the post's access restrictions.
|
||||
*
|
||||
* @param array $caps
|
||||
* @param string $cap
|
||||
* @param int $user_id
|
||||
* @param array $args
|
||||
* @return array
|
||||
*/
|
||||
public static function map_meta_cap( $caps, $cap, $user_id, $args ) {
|
||||
if ( isset( $args[0] ) ) {
|
||||
if ( strpos( $cap, 'edit_' ) === 0 || strpos( $cap, 'delete_' ) === 0 ) {
|
||||
if ( $post_type = get_post_type( $args[0] ) ) {
|
||||
|
||||
$edit_post_type = 'edit_' . $post_type;
|
||||
$delete_post_type = 'delete_' . $post_type;
|
||||
if ( $post_type_object = get_post_type_object( $post_type ) ) {
|
||||
if ( !isset( $post_type_object->capabilities ) ) {
|
||||
$post_type_object->capabilities = array();
|
||||
}
|
||||
$caps_object = get_post_type_capabilities( $post_type_object );
|
||||
if ( isset( $caps_object->edit_post ) ) {
|
||||
$edit_post_type = $caps_object->edit_post;
|
||||
}
|
||||
if ( isset( $caps_object->delete_post ) ) {
|
||||
$delete_post_type = $caps_object->delete_post;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $cap === $edit_post_type || $cap === $delete_post_type ) {
|
||||
$post_id = null;
|
||||
if ( is_numeric( $args[0] ) ) {
|
||||
$post_id = $args[0];
|
||||
} else if ( $args[0] instanceof WP_Post ) {
|
||||
$post_id = $args[0]->ID;
|
||||
}
|
||||
if ( $post_id ) {
|
||||
if ( !self::user_can_read_post( $post_id, $user_id ) ) {
|
||||
$caps[] = 'do_not_allow';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $caps;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters out posts that the user should not be able to access.
|
||||
*
|
||||
* @param string $where current where conditions
|
||||
* @param WP_Query $query current query
|
||||
* @return string modified $where
|
||||
*/
|
||||
public static function posts_where( $where, $query ) {
|
||||
|
||||
global $wpdb;
|
||||
|
||||
if ( apply_filters( 'groups_post_access_posts_where_apply', true, $where, $query ) ) {
|
||||
|
||||
$user_id = get_current_user_id();
|
||||
|
||||
// this only applies to logged in users
|
||||
if ( _groups_admin_override() ) {
|
||||
return $where;
|
||||
}
|
||||
|
||||
// Groups admins see everything
|
||||
if ( current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
|
||||
return $where;
|
||||
}
|
||||
|
||||
if ( !apply_filters( 'groups_post_access_posts_where_filter_all', false ) ) {
|
||||
$filter = true;
|
||||
$post_types = apply_filters(
|
||||
'groups_post_access_posts_where_query_get_post_types',
|
||||
$query->get( 'post_type', null ),
|
||||
$where,
|
||||
$query
|
||||
);
|
||||
if ( 'any' == $post_types ) {
|
||||
// we need to filter in this case as it affects any post type
|
||||
} else if ( !empty( $post_types ) && is_array( $post_types ) ) {
|
||||
// if there is at least one post type we handle, we need to filter
|
||||
$handled = 0;
|
||||
$handles_post_types = self::get_handles_post_types();
|
||||
foreach( $post_types as $post_type ) {
|
||||
if ( !isset( $handles_post_types[$post_type] ) || $handles_post_types[$post_type] ) {
|
||||
$handled++;
|
||||
}
|
||||
}
|
||||
$filter = $handled > 0;
|
||||
} else if ( !empty( $post_types ) && is_string( $post_types ) ) {
|
||||
$filter = self::handles_post_type( $post_types );
|
||||
} else if ( $query->is_attachment ) {
|
||||
$filter = self::handles_post_type( 'attachment' );
|
||||
} else if ( $query->is_page ) {
|
||||
$filter = self::handles_post_type( 'page' );
|
||||
} else {
|
||||
$filter = self::handles_post_type( 'post' );
|
||||
}
|
||||
if ( !$filter ) {
|
||||
return $where;
|
||||
}
|
||||
}
|
||||
|
||||
$handles_post_types = Groups_Post_Access::get_handles_post_types();
|
||||
$post_types = array();
|
||||
foreach( $handles_post_types as $post_type => $handles ) {
|
||||
if ( $handles ) {
|
||||
$post_types[] = $post_type;
|
||||
}
|
||||
}
|
||||
if ( count( $post_types ) == 0 ) {
|
||||
return $where;
|
||||
}
|
||||
$post_types_in = "'" . implode( "','", array_map( 'esc_sql', $post_types ) ) . "'";
|
||||
|
||||
// 1. Get all the groups that the user belongs to, including those that are inherited:
|
||||
$group_ids = array();
|
||||
if ( $user = new Groups_User( $user_id ) ) {
|
||||
$group_ids_deep = $user->group_ids_deep;
|
||||
if ( is_array( $group_ids_deep ) ) {
|
||||
$group_ids = $group_ids_deep;
|
||||
}
|
||||
}
|
||||
|
||||
if ( count( $group_ids ) > 0 ) {
|
||||
$group_ids = "'" . implode( "','", $group_ids ) . "'";
|
||||
} else {
|
||||
$group_ids = '\'\'';
|
||||
}
|
||||
|
||||
// 2. Filter the posts:
|
||||
// This allows the user to access posts where the posts are not restricted or where
|
||||
// the user belongs to ANY of the groups:
|
||||
// $where .= sprintf(
|
||||
// " AND {$wpdb->posts}.ID IN " .
|
||||
// " ( " .
|
||||
// " SELECT ID FROM $wpdb->posts WHERE post_type NOT IN (%s) OR ID NOT IN ( SELECT post_id FROM $wpdb->postmeta WHERE {$wpdb->postmeta}.meta_key = '%s' ) " . // posts of a type that is not handled or posts without access restriction
|
||||
// " UNION ALL " . // we don't care about duplicates here, just make it quick
|
||||
// " SELECT post_id AS ID FROM $wpdb->postmeta WHERE {$wpdb->postmeta}.meta_key = '%s' AND {$wpdb->postmeta}.meta_value IN (%s) " . // posts that require any group the user belongs to
|
||||
// " ) ",
|
||||
// $post_types_in,
|
||||
// self::POSTMETA_PREFIX . self::READ,
|
||||
// self::POSTMETA_PREFIX . self::READ,
|
||||
// $group_ids
|
||||
// );
|
||||
// New faster version - Exclude any post IDs from:
|
||||
// posts restricted to groups that the user does not belong to MINUS posts restricted to groups to which the user belongs
|
||||
$where .= sprintf(
|
||||
" AND {$wpdb->posts}.ID NOT IN ( " .
|
||||
"SELECT ID FROM $wpdb->posts WHERE " .
|
||||
"post_type IN (%s) AND " .
|
||||
"ID IN ( " .
|
||||
"SELECT post_id FROM $wpdb->postmeta pm WHERE " .
|
||||
"pm.meta_key = '%s' AND pm.meta_value NOT IN (%s) AND " .
|
||||
"post_id NOT IN ( SELECT post_id FROM $wpdb->postmeta pm WHERE pm.meta_key = '%s' AND pm.meta_value IN (%s) ) " .
|
||||
") " .
|
||||
") ",
|
||||
$post_types_in,
|
||||
esc_sql( self::POSTMETA_PREFIX . self::READ ),
|
||||
$group_ids,
|
||||
esc_sql( self::POSTMETA_PREFIX . self::READ ),
|
||||
$group_ids
|
||||
);
|
||||
}
|
||||
|
||||
return apply_filters( 'groups_post_access_posts_where', $where, $query );
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter pages by access capability.
|
||||
*
|
||||
* @param array $pages
|
||||
*/
|
||||
public static function get_pages( $pages ) {
|
||||
$result = array();
|
||||
if ( apply_filters( 'groups_post_access_get_pages_apply', true, $pages ) ) {
|
||||
$user_id = get_current_user_id();
|
||||
foreach ( $pages as $page ) {
|
||||
if ( self::user_can_read_post( $page->ID, $user_id ) ) {
|
||||
$result[] = $page;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$result = $pages;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter posts by access capability.
|
||||
*
|
||||
* @param array $posts list of posts
|
||||
* @param WP_Query $query
|
||||
*/
|
||||
public static function the_posts( $posts, &$query ) {
|
||||
$result = array();
|
||||
if ( apply_filters( 'groups_post_access_the_posts_apply', true, $posts, $query ) ) {
|
||||
$user_id = get_current_user_id();
|
||||
foreach ( $posts as $post ) {
|
||||
if ( self::user_can_read_post( $post->ID, $user_id ) ) {
|
||||
$result[] = $post;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$result = $posts;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter menu items by access capability.
|
||||
*
|
||||
* @todo admin section: this won't inhibit the items being offered to be added, although when they're added they won't show up in the menu
|
||||
*
|
||||
* @param array $items
|
||||
* @param mixed $menu
|
||||
* @param array $args
|
||||
*/
|
||||
public static function wp_get_nav_menu_items( $items = null, $menu = null, $args = null ) {
|
||||
$result = array();
|
||||
if ( apply_filters( 'groups_post_access_wp_get_nav_menu_items_apply', true, $items, $menu, $args ) ) {
|
||||
$user_id = get_current_user_id();
|
||||
foreach ( $items as $item ) {
|
||||
// @todo might want to check $item->object and $item->type first,
|
||||
// for example these are 'page' and 'post_type' for a page
|
||||
if ( self::user_can_read_post( $item->object_id, $user_id ) ) {
|
||||
$result[] = $item;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$result = $items;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter excerpt by access capability.
|
||||
*
|
||||
* @param string $output
|
||||
* @return $output if access granted, otherwise ''
|
||||
*/
|
||||
public static function get_the_excerpt( $output ) {
|
||||
global $post;
|
||||
$result = '';
|
||||
if ( apply_filters( 'groups_post_access_get_the_excerpt_apply', true, $output ) ) {
|
||||
if ( isset( $post->ID ) ) {
|
||||
if ( self::user_can_read_post( $post->ID ) ) {
|
||||
$result = $output;
|
||||
}
|
||||
} else {
|
||||
// not a post, don't interfere
|
||||
$result = $output;
|
||||
}
|
||||
} else {
|
||||
$result = $output;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter content by access capability.
|
||||
*
|
||||
* @param string $output
|
||||
* @return $output if access granted, otherwise ''
|
||||
*/
|
||||
public static function the_content( $output ) {
|
||||
global $post;
|
||||
$result = '';
|
||||
if ( apply_filters( 'groups_post_access_the_content_apply', true, $output ) ) {
|
||||
if ( isset( $post->ID ) ) {
|
||||
if ( self::user_can_read_post( $post->ID ) ) {
|
||||
$result = $output;
|
||||
}
|
||||
} else {
|
||||
// not a post, don't interfere
|
||||
$result = $output;
|
||||
}
|
||||
} else {
|
||||
$result = $output;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooked on the get_{$adjacent}_post_where filter to remove restricted posts.
|
||||
*
|
||||
* @param string $where
|
||||
* @param boolean $in_same_term
|
||||
* @param array $excluded_terms
|
||||
* @param string $taxonomy
|
||||
* @param WP_Post $post
|
||||
*
|
||||
* @return string $where modified if appropriate
|
||||
*/
|
||||
public static function get_previous_post_where( $where, $in_same_term, $excluded_terms, $taxonomy, $post ) {
|
||||
return self::get_next_post_where( $where, $in_same_term, $excluded_terms, $taxonomy, $post );
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooked on the get_{$adjacent}_post_where filter to remove restricted posts.
|
||||
*
|
||||
* @param string $where
|
||||
* @param boolean $in_same_term
|
||||
* @param array $excluded_terms
|
||||
* @param string $taxonomy
|
||||
* @param WP_Post $post
|
||||
*
|
||||
* @return string $where modified if appropriate
|
||||
*/
|
||||
public static function get_next_post_where( $where, $in_same_term, $excluded_terms, $taxonomy, $post ) {
|
||||
if ( !empty( $post ) ) {
|
||||
// run it through get_posts with suppress_filters set to false so that our posts_where filter is applied and assures only accessible posts are seen
|
||||
$post_ids = get_posts( array( 'post_type' => $post->post_type, 'numberposts' => -1, 'suppress_filters' => false, 'fields' => 'ids' ) );
|
||||
if ( is_array( $post_ids ) && count( $post_ids ) > 0 ) {
|
||||
$post_ids = array_map( 'intval', $post_ids );
|
||||
$condition = ' p.ID IN (' . implode( ',', $post_ids ) . ') ';
|
||||
if ( !empty( $where ) ) {
|
||||
$where .= ' AND ' . $condition;
|
||||
} else {
|
||||
$where = ' WHERE ' . $condition;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $where;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an access requirement based on post_id and group_id.
|
||||
*
|
||||
* (*) Revisions : As of Groups 1.3.13 and at WordPress 3.6.1, as
|
||||
* add_post_meta stores postmeta for the revision's parent, we retrieve
|
||||
* the parent's post ID if it applies and check against that to see if
|
||||
* that capability is already present. This is to avoid duplicating
|
||||
* the already existing postmeta entry (which ocurred in previous
|
||||
* versions).
|
||||
*
|
||||
* @param array $map must contain 'post_id' (*) and 'group_id'
|
||||
* @return true if the capability could be added to the post, otherwise false
|
||||
*/
|
||||
public static function create( $map ) {
|
||||
extract( $map );
|
||||
$result = false;
|
||||
|
||||
if ( isset( $capability ) ) {
|
||||
_doing_it_wrong(
|
||||
__CLASS__ . '::' . __METHOD__,
|
||||
__( 'You should use Groups_Post_Access_Legacy::create() to pass a capability restriction instead.', 'groups' ),
|
||||
'2.0.0'
|
||||
);
|
||||
}
|
||||
|
||||
if ( !empty( $post_id ) && !empty( $group_id ) ) {
|
||||
$post_id = Groups_Utility::id( $post_id );
|
||||
$group_id = Groups_Utility::id( $group_id );
|
||||
if ( Groups_Group::read( $group_id ) ) {
|
||||
if ( $revision_parent_id = wp_is_post_revision( $post_id ) ) {
|
||||
$post_id = $revision_parent_id;
|
||||
}
|
||||
if ( !in_array( $group_id, get_post_meta( $post_id, self::POSTMETA_PREFIX . self::READ ) ) ) {
|
||||
$result = add_post_meta( $post_id, self::POSTMETA_PREFIX . self::READ, $group_id );
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the post requires the user to be a member of the given group(s) to grant access.
|
||||
*
|
||||
* @param int $post_id ID of the post
|
||||
* @param array $map should provide 'post_id' and 'groups_read'
|
||||
*
|
||||
* @return true if the group(s) is required, otherwise false
|
||||
*/
|
||||
public static function read( $post_id, $map = array() ) {
|
||||
extract( $map );
|
||||
$result = false;
|
||||
if ( !empty( $post_id ) ) {
|
||||
if ( isset( $groups_read ) ) {
|
||||
if ( empty( $groups_read ) ) {
|
||||
$groups_read = array();
|
||||
} else if ( !is_array( $groups_read ) ) {
|
||||
$groups_read = array( $groups_read );
|
||||
}
|
||||
$group_ids = get_post_meta( $post_id, self::POSTMETA_PREFIX . self::READ );
|
||||
if ( $group_ids ) {
|
||||
foreach( $groups_read as $group_id ) {
|
||||
$result = in_array( $group_id, $group_ids );
|
||||
if ( !$result ) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the post access restrictions.
|
||||
*
|
||||
* $map must provide 'post_id' (int) indicating the post's ID and 'groups_read' (int|array of int) holding group IDs that restrict read access.
|
||||
*
|
||||
* @param array $map
|
||||
* @return array of group ids, false on failure
|
||||
*/
|
||||
public static function update( $map ) {
|
||||
extract( $map );
|
||||
$result = false;
|
||||
if ( !empty( $post_id ) ) {
|
||||
if ( empty( $groups_read ) ) {
|
||||
$groups_read = array();
|
||||
} else if ( !is_array( $groups_read ) ) {
|
||||
$groups_read = array( $groups_read );
|
||||
}
|
||||
$groups_read = array_map( array( 'Groups_Utility', 'id' ), $groups_read );
|
||||
$current_groups_read = get_post_meta( $post_id, self::POSTMETA_PREFIX . self::READ );
|
||||
$current_groups_read = array_map( array( 'Groups_Utility', 'id' ), $current_groups_read );
|
||||
foreach( $groups_read as $group_id ) {
|
||||
if ( !in_array( $group_id, $current_groups_read ) ) {
|
||||
add_post_meta( $post_id, self::POSTMETA_PREFIX . self::READ, $group_id );
|
||||
}
|
||||
}
|
||||
foreach( $current_groups_read as $group_id ) {
|
||||
if ( !in_array( $group_id, $groups_read ) ) {
|
||||
delete_post_meta( $post_id, self::POSTMETA_PREFIX . self::READ, $group_id );
|
||||
}
|
||||
}
|
||||
$result = array_map( array( 'Groups_Utility', 'id' ), get_post_meta( $post_id, self::POSTMETA_PREFIX . self::READ ) );
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a access restrictions from a post.
|
||||
*
|
||||
* @param int $post_id
|
||||
* @param array $map must provide 'groups_read' holding group IDs to remove from restricting access to the post; if empty, all access restrictions will be removed
|
||||
* @return true on success, otherwise false
|
||||
*/
|
||||
public static function delete( $post_id, $map = array() ) {
|
||||
extract( $map );
|
||||
$result = false;
|
||||
if ( !empty( $post_id ) ) {
|
||||
if ( empty( $groups_read ) ) {
|
||||
$groups_read = array();
|
||||
} else if ( !is_array( $groups_read ) ) {
|
||||
$groups_read = array( $groups_read );
|
||||
}
|
||||
$groups_read = array_map( array( 'Groups_Utility', 'id' ), $groups_read );
|
||||
if ( !empty( $groups_read ) ) {
|
||||
foreach( $groups_read as $group_id ) {
|
||||
$result = delete_post_meta( $post_id, self::POSTMETA_PREFIX . self::READ, $group_id );
|
||||
}
|
||||
} else {
|
||||
$result = delete_post_meta( $post_id, self::POSTMETA_PREFIX . self::READ );
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of capabilities that grant access to the post.
|
||||
*
|
||||
* @deprecated
|
||||
* @param int $post_id
|
||||
* @return array of string, capabilities
|
||||
*/
|
||||
public static function get_read_post_capabilities( $post_id ) {
|
||||
_doing_it_wrong(
|
||||
__CLASS__ . '::' . __METHOD__,
|
||||
__( 'This method is deprecated. You should use Groups_Post_Access_Legacy::get_read_post_capabilities() to retrieve the capabilities instead.', 'groups' ),
|
||||
'2.0.0'
|
||||
);
|
||||
|
||||
require_once( GROUPS_LEGACY_LIB . '/access/class-groups-post-access-legacy.php' );
|
||||
return Groups_Post_Access_Legacy::get_read_post_capabilities( $post_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of group IDs that grant read access to the post.
|
||||
*
|
||||
* @param int $post_id
|
||||
* @return array of int, group IDs
|
||||
*/
|
||||
public static function get_read_group_ids( $post_id ) {
|
||||
return get_post_meta( $post_id, self::POSTMETA_PREFIX . self::READ );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the user belongs to any of the groups that grant access to the post.
|
||||
*
|
||||
* @param int $post_id post id
|
||||
* @param int $user_id user id or null for current user
|
||||
* @return boolean true if user can read the post
|
||||
*/
|
||||
public static function user_can_read_post( $post_id, $user_id = null ) {
|
||||
|
||||
$result = false;
|
||||
|
||||
if ( !empty( $post_id ) ) {
|
||||
|
||||
if ( $user_id === null ) {
|
||||
$user_id = get_current_user_id();
|
||||
}
|
||||
|
||||
$cached = Groups_Cache::get( self::CAN_READ_POST . '_' . $user_id . '_' . $post_id, self::CACHE_GROUP );
|
||||
|
||||
if ( $cached !== null ) {
|
||||
$result = $cached->value;
|
||||
unset( $cached );
|
||||
} else {
|
||||
// admin override and Groups admins see everything
|
||||
if ( _groups_admin_override() || current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
|
||||
$result = true;
|
||||
} else {
|
||||
// can read if post type is not handled
|
||||
if ( $post_type = get_post_type( $post_id ) ) {
|
||||
if ( !self::handles_post_type( $post_type ) ) {
|
||||
$result = true;
|
||||
}
|
||||
}
|
||||
// check if the user can read
|
||||
if ( !$result ) {
|
||||
$groups_user = new Groups_User( $user_id );
|
||||
$group_ids = self::get_read_group_ids( $post_id );
|
||||
if ( empty( $group_ids ) ) {
|
||||
$result = true;
|
||||
} else {
|
||||
$ids = array_intersect( $groups_user->group_ids_deep, $group_ids );
|
||||
$result = !empty( $ids );
|
||||
}
|
||||
}
|
||||
}
|
||||
$result = apply_filters( 'groups_post_access_user_can_read_post', $result, $post_id, $user_id );
|
||||
Groups_Cache::set( self::CAN_READ_POST . '_' . $user_id . '_' . $post_id, $result, self::CACHE_GROUP );
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooks into groups_deleted_group to remove existing access restrictions
|
||||
* based on the deleted group.
|
||||
*
|
||||
* @param int $group_id the ID of the deleted group
|
||||
*/
|
||||
public static function groups_deleted_group( $group_id ) {
|
||||
if ( $group_id ) {
|
||||
delete_metadata( 'post', null, self::POSTMETA_PREFIX . self::READ, $group_id, true );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooked on wp_count_posts to correct the post counts.
|
||||
*
|
||||
* Note: at WP 4.7.4 through WP_Posts_List_Table::prepare_items() which obtains $post_status
|
||||
* independent of the post type, we will come here for any post status, so don't be surprised
|
||||
* to see this executed e.g. on post type 'post' with e.g. 'wc-completed' post status.
|
||||
*
|
||||
* @param object $counts An object containing the current post_type's post counts by status.
|
||||
* @param string $type the post type
|
||||
* @param string $perm The permission to determine if the posts are 'readable' by the current user.
|
||||
*/
|
||||
public static function wp_count_posts( $counts, $type, $perm ) {
|
||||
$user_id = get_current_user_id();
|
||||
$cached = Groups_Cache::get( self::COUNT_POSTS . '_' . $type . '_' . $user_id, self::CACHE_GROUP );
|
||||
if ( $cached !== null ) {
|
||||
$counts = $cached->value;
|
||||
unset( $cached );
|
||||
} else {
|
||||
if ( self::handles_post_type( $type ) ) {
|
||||
foreach( $counts as $post_status => $count ) {
|
||||
$query_args = array(
|
||||
'fields' => 'ids',
|
||||
'post_type' => $type,
|
||||
'post_status' => $post_status,
|
||||
'numberposts' => -1, // all
|
||||
'suppress_filters' => 0, // don't suppres filters as we need to get restrictions taken into account
|
||||
'orderby' => 'none', // Important! Don't waste time here.
|
||||
'no_found_rows' => true, // performance, omit unnecessary SQL_CALC_FOUND_ROWS in query here
|
||||
'nopaging' => true // no paging is needed, in case it would affect performance
|
||||
);
|
||||
// WooCommerce Orders
|
||||
if ( function_exists( 'wc_get_order_statuses' ) && ( $type == 'shop_order' ) ) {
|
||||
$wc_order_statuses = array_keys( wc_get_order_statuses() );
|
||||
if ( !in_array( $post_status, $wc_order_statuses ) ) {
|
||||
// Skip getting the post count for this status as it's
|
||||
// not a valid order status and WC would raise a PHP Notice.
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// WooCommerce Subscriptions
|
||||
if ( function_exists( 'wcs_get_subscription_statuses' ) && ( $type == 'shop_subscription' ) ) {
|
||||
$wc_subscription_statuses = array_keys( wcs_get_subscription_statuses() );
|
||||
if ( !in_array( $post_status, $wc_subscription_statuses ) ) {
|
||||
// Skip as it's not a valid subscription status
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$posts = get_posts( $query_args );
|
||||
$count = count( $posts );
|
||||
unset( $posts );
|
||||
$counts->$post_status = $count;
|
||||
}
|
||||
}
|
||||
Groups_Cache::set( self::COUNT_POSTS . '_' . $type . '_' . $user_id, $counts, self::CACHE_GROUP );
|
||||
}
|
||||
return $counts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Would be hooked on wp_count_attachments to correct the counts but it's not actually
|
||||
* being used in the current media library.
|
||||
*
|
||||
* @param object $counts An object containing the attachment counts by mime type.
|
||||
* @param string $mime_type The mime type pattern used to filter the attachments counted.
|
||||
*/
|
||||
public static function wp_count_attachments( $counts, $mime_type ) {
|
||||
return $counts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if we are supposed to handle the post type, otherwise false.
|
||||
*
|
||||
* @param string $post_type
|
||||
* @return boolean
|
||||
*/
|
||||
public static function handles_post_type( $post_type ) {
|
||||
$post_types = self::get_handles_post_types();
|
||||
return isset( $post_types[$post_type] ) && $post_types[$post_type];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of post types indicating for each whether we handle it (true) or not.
|
||||
* The array is indexed by the post type names.
|
||||
*
|
||||
* @return array indexed by post type names, indicating the value true if we handle it, otherwise false
|
||||
*/
|
||||
public static function get_handles_post_types() {
|
||||
$result = array();
|
||||
$post_types_option = Groups_Options::get_option( self::POST_TYPES, array() );
|
||||
$post_types = get_post_types( array(), 'objects' );
|
||||
foreach( $post_types as $post_type => $object ) {
|
||||
$public = isset( $object->public ) ? $object->public : false;
|
||||
$exclude_from_search = isset( $object->exclude_from_search ) ? $object->exclude_from_search : false;
|
||||
$publicly_queryable = isset( $object->publicly_queryable ) ? $object->publicly_queryable : false;
|
||||
$show_ui = isset( $object->show_ui ) ? $object->show_ui : false;
|
||||
$show_in_nav_menus = isset( $object->show_in_nav_menus ) ? $object->show_in_nav_menus : false;
|
||||
|
||||
// by default, handle any post type whose public attribute is true
|
||||
$managed =
|
||||
$public && ( !isset( $post_types_option[$post_type] ) || !isset( $post_types_option[$post_type]['add_meta_box'] ) ) ||
|
||||
isset( $post_types_option[$post_type] ) && isset( $post_types_option[$post_type]['add_meta_box'] ) && $post_types_option[$post_type]['add_meta_box'];
|
||||
$result[$post_type] = $managed;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set which post types we should handle.
|
||||
*
|
||||
* @param array $post_types of post type names mapped to booleans, indicating to handle or not a post type
|
||||
*/
|
||||
public static function set_handles_post_types( $post_types ) {
|
||||
$post_types_option = Groups_Options::get_option( self::POST_TYPES, array() );
|
||||
$available_post_types = get_post_types();
|
||||
foreach( $available_post_types as $post_type ) {
|
||||
$post_types_option[$post_type]['add_meta_box'] = isset( $post_types[$post_type] ) && $post_types[$post_type];
|
||||
}
|
||||
Groups_Options::update_option( self::POST_TYPES, $post_types_option );
|
||||
}
|
||||
}
|
||||
Groups_Post_Access::init();
|
||||
@@ -0,0 +1,167 @@
|
||||
<?php
|
||||
/**
|
||||
* class-groups-admin-notice.php
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author itthinx
|
||||
* @package groups
|
||||
* @since 2.2.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Notices
|
||||
*/
|
||||
class Groups_Admin_Notice {
|
||||
|
||||
/**
|
||||
* Time mark.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const INIT_TIME = 'groups-init-time';
|
||||
|
||||
/**
|
||||
* Used to store user meta and hide the notice asking to review.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const HIDE_REVIEW_NOTICE = 'groups-hide-review-notice';
|
||||
|
||||
/**
|
||||
* Used to check next time.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const REMIND_LATER_NOTICE = 'groups-remind-later-notice';
|
||||
|
||||
/**
|
||||
* The number of seconds in seven days, since init date to show the notice.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const SHOW_LAPSE = 604800;
|
||||
|
||||
/**
|
||||
* The number of seconds in one day, used to show notice later again.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const REMIND_LAPSE = 86400;
|
||||
|
||||
/**
|
||||
* Adds actions.
|
||||
*/
|
||||
public static function init() {
|
||||
add_action( 'admin_init', array( __CLASS__,'admin_init' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooked on the admin_init action.
|
||||
*/
|
||||
public static function admin_init() {
|
||||
if ( current_user_can( 'activate_plugins' ) ) {
|
||||
$user_id = get_current_user_id();
|
||||
if ( !empty( $_GET[self::HIDE_REVIEW_NOTICE] ) && wp_verify_nonce( $_GET['groups_notice'], 'hide' ) ) {
|
||||
add_user_meta( $user_id, self::HIDE_REVIEW_NOTICE, true );
|
||||
}
|
||||
if ( !empty( $_GET[self::REMIND_LATER_NOTICE] ) && wp_verify_nonce( $_GET['groups_notice'], 'later' ) ) {
|
||||
update_user_meta( $user_id, self::REMIND_LATER_NOTICE, time() + self::REMIND_LAPSE );
|
||||
}
|
||||
$hide_review_notice = get_user_meta( $user_id, self::HIDE_REVIEW_NOTICE, true );
|
||||
if ( empty( $hide_review_notice ) ) {
|
||||
$d = time() - self::get_init_time();
|
||||
if ( $d >= self::SHOW_LAPSE ) {
|
||||
$remind_later_notice = get_user_meta( $user_id, self::REMIND_LATER_NOTICE, true );
|
||||
if ( empty( $remind_later_notice ) || ( time() > $remind_later_notice ) ) {
|
||||
add_action( 'admin_notices', array( __CLASS__, 'admin_notices' ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes if necessary and returns the init time.
|
||||
*/
|
||||
public static function get_init_time() {
|
||||
$init_time = get_site_option( self::INIT_TIME, null );
|
||||
if ( $init_time === null ) {
|
||||
$init_time = time();
|
||||
add_site_option( self::INIT_TIME, $init_time );
|
||||
}
|
||||
return $init_time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the admin notice.
|
||||
*/
|
||||
public static function admin_notices() {
|
||||
|
||||
$current_url = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
|
||||
$hide_url = wp_nonce_url( add_query_arg( self::HIDE_REVIEW_NOTICE, true, $current_url ), 'hide', 'groups_notice' );
|
||||
$remind_url = wp_nonce_url( add_query_arg( self::REMIND_LATER_NOTICE, true, $current_url ), 'later', 'groups_notice' );
|
||||
|
||||
$output = '';
|
||||
|
||||
$output .= '<style type="text/css">';
|
||||
$output .= 'div.groups-rating {';
|
||||
$output .= sprintf( 'background: url(%s) #fff no-repeat 8px 8px;', GROUPS_PLUGIN_URL . '/images/groups-256x256.png' );
|
||||
$output .= 'padding-left: 76px ! important;';
|
||||
$output .= 'background-size: 64px 64px;';
|
||||
$output .= '}';
|
||||
$output .= '</style>';
|
||||
|
||||
$output .= '<div class="updated groups-rating">';
|
||||
$output .= '<p>';
|
||||
$output .= __( 'Many thanks for using <strong>Groups</strong>!', 'groups' );
|
||||
$output .= ' ';
|
||||
$output .= __( 'Could you please spare a minute and give it a review over at WordPress.org?', 'groups' );
|
||||
$output .= ' ';
|
||||
$output .= sprintf(
|
||||
'<a style="color:inherit;white-space:nowrap;" href="%s">%s</a>',
|
||||
esc_url( $hide_url ),
|
||||
esc_html( __( 'I have already done that.', 'groups' ) )
|
||||
);
|
||||
$output .= '</p>';
|
||||
$output .= '<p>';
|
||||
$output .= sprintf(
|
||||
'<a class="button button-primary" href="%s" target="_blank">%s</a>',
|
||||
esc_url( 'http://wordpress.org/support/view/plugin-reviews/groups?filter=5#postform' ),
|
||||
__( 'Yes, here we go!', 'groups' )
|
||||
);
|
||||
$output .= ' ';
|
||||
$output .= sprintf(
|
||||
'<a class="button" href="%s">%s</a>',
|
||||
esc_url( $remind_url ),
|
||||
esc_html( __( 'Remind me later', 'groups' ) )
|
||||
);
|
||||
|
||||
$output .= '</p>';
|
||||
$output .= '<p>';
|
||||
$output .= sprintf(
|
||||
__( 'You can also follow <a href="%s">@itthinx</a> on Twitter or visit <a href="%s" target="_blank">itthinx.com</a> to check out other free and premium plugins we provide.', 'groups' ),
|
||||
esc_url( 'https://twitter.com/itthinx' ),
|
||||
esc_url( 'https://www.itthinx.com' )
|
||||
);
|
||||
$output .= '</p>';
|
||||
$output .= '</div>';
|
||||
|
||||
echo $output;
|
||||
}
|
||||
}
|
||||
Groups_Admin_Notice::init();
|
||||
@@ -0,0 +1,222 @@
|
||||
<?php
|
||||
/**
|
||||
* class-groups-admin-custom-posts.php
|
||||
*
|
||||
* Copyright (c) 2012 "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Antonio Blanco
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.4.2
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Post column extensions.
|
||||
*/
|
||||
class Groups_Admin_Post_Columns {
|
||||
|
||||
/**
|
||||
* Groups column header id.
|
||||
* @var string
|
||||
*/
|
||||
const GROUPS = 'groups-read';
|
||||
|
||||
/**
|
||||
* Field name.
|
||||
* @var string
|
||||
*/
|
||||
const GROUPS_READ = 'groups-read';
|
||||
|
||||
const CACHE_GROUP = 'groups';
|
||||
const EDIT_TERM_LINK = 'edit-term-link';
|
||||
|
||||
/**
|
||||
* Adds an admin_init action.
|
||||
*/
|
||||
public static function init() {
|
||||
add_action( 'admin_init', array( __CLASS__, 'admin_init' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the filters and actions only for users who have the right
|
||||
* Groups permissions and for the post types that have access
|
||||
* restrictions enabled.
|
||||
*/
|
||||
public static function admin_init() {
|
||||
if ( current_user_can( GROUPS_ACCESS_GROUPS ) ) {
|
||||
$post_types = get_post_types( array( 'public' => true ) );
|
||||
$post_types_option = Groups_Options::get_option( Groups_Post_Access::POST_TYPES, array() );
|
||||
foreach ( $post_types as $post_type ) {
|
||||
if ( !isset( $post_types_option[$post_type]['add_meta_box'] ) || $post_types_option[$post_type]['add_meta_box'] ) {
|
||||
if ( ( $post_type == 'attachment' ) ) {
|
||||
// filters to display the media's access restriction groups
|
||||
add_filter( 'manage_media_columns', array( __CLASS__, 'columns' ) );
|
||||
// args: string $column_name, int $media_id
|
||||
add_action( 'manage_media_custom_column', array( __CLASS__, 'custom_column' ), 10, 2 );
|
||||
// make the groups column sortable
|
||||
add_filter( 'manage_upload_sortable_columns', array( __CLASS__, 'manage_edit_post_sortable_columns' ) );
|
||||
} else {
|
||||
// filters to display the posts' access restriction groups
|
||||
add_filter( 'manage_' . $post_type . '_posts_columns', array( __CLASS__, 'columns' ) );
|
||||
// args: string $column_name, int $post_id
|
||||
add_action( 'manage_' . $post_type . '_posts_custom_column', array( __CLASS__, 'custom_column' ), 10, 2 );
|
||||
// make the groups column sortable
|
||||
add_filter( 'manage_edit-' . $post_type . '_sortable_columns', array( __CLASS__, 'manage_edit_post_sortable_columns' ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new column to the post type's table showing the access
|
||||
* restriction groups.
|
||||
*
|
||||
* @param array $column_headers
|
||||
* @return array column headers
|
||||
*/
|
||||
public static function columns( $column_headers ) {
|
||||
$column_headers[self::GROUPS] = sprintf(
|
||||
'<span title="%s">%s</span>',
|
||||
esc_attr( __( 'One or more groups granting access to entries.', 'groups' ) ),
|
||||
esc_html( _x( 'Groups', 'Column header', 'groups' ) )
|
||||
);
|
||||
return $column_headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders custom column content.
|
||||
*
|
||||
* @param string $column_name
|
||||
* @param int $post_id
|
||||
* @return string custom column content
|
||||
*/
|
||||
public static function custom_column( $column_name, $post_id ) {
|
||||
$output = '';
|
||||
switch ( $column_name ) {
|
||||
case self::GROUPS :
|
||||
$entries = array();
|
||||
$groups_read = get_post_meta( $post_id, Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ );
|
||||
if ( count( $groups_read ) > 0 ) {
|
||||
$groups = Groups_Group::get_groups( array( 'order_by' => 'name', 'order' => 'ASC', 'include' => $groups_read ) );
|
||||
if ( ( count( $groups ) > 0 ) ) {
|
||||
foreach( $groups as $group ) {
|
||||
$entries[] = wp_strip_all_tags( $group->name );
|
||||
}
|
||||
}
|
||||
}
|
||||
if (
|
||||
function_exists( 'get_term_meta' ) && // >= WordPress 4.4
|
||||
class_exists( 'Groups_Restrict_Categories' ) &&
|
||||
method_exists( 'Groups_Restrict_Categories', 'get_controlled_taxonomies' ) &&
|
||||
method_exists( 'Groups_Restrict_Categories', 'get_term_read_groups' ) // >= Groups Restrict Categories 2.0.0
|
||||
) {
|
||||
$taxonomies = Groups_Restrict_Categories::get_controlled_taxonomies();
|
||||
if ( count( $taxonomies ) > 0 ) {
|
||||
$terms = wp_get_object_terms( $post_id, $taxonomies );
|
||||
if ( !( $terms instanceof WP_Error ) ) {
|
||||
foreach( $terms as $term ) {
|
||||
if ( in_array( $term->taxonomy, $taxonomies ) ) {
|
||||
$term_group_ids = Groups_Restrict_Categories::get_term_read_groups( $term->term_id );
|
||||
if ( count( $term_group_ids ) > 0 ) {
|
||||
|
||||
if ( !empty( $term_group_ids ) ) {
|
||||
$edit_term_link = self::get_edit_term_link( $term->term_id, $term->taxonomy );
|
||||
$taxonomy_label = '';
|
||||
if ( $taxonomy = get_taxonomy( $term->taxonomy ) ) {
|
||||
$taxonomy_label = isset( $taxonomy->label ) ? __( $taxonomy->label ) : '';
|
||||
$labels = isset( $taxonomy->labels ) ? $taxonomy->labels : null;
|
||||
if ( $labels !== null ) {
|
||||
if ( isset( $labels->singular_name ) ) {
|
||||
$taxonomy_label = __( $labels->singular_name );
|
||||
}
|
||||
}
|
||||
}
|
||||
$term_taxonomy_title = !empty( $term->name ) ? $term->name : '';
|
||||
$term_taxonomy_title.= !empty( $taxonomy_label ) ? ' ' . $taxonomy_label : '';
|
||||
foreach( $term_group_ids as $group_id ) {
|
||||
if ( $group = Groups_Group::read( $group_id ) ) {
|
||||
$entries[] = sprintf(
|
||||
'%s <a href="%s" title="%s" style="cursor: help">%s</a>',
|
||||
esc_html( $group->name ),
|
||||
esc_url( $edit_term_link ),
|
||||
esc_attr( $term_taxonomy_title),
|
||||
esc_html( $term->name )
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( !empty( $entries ) ) {
|
||||
sort( $entries );
|
||||
$output .= '<ul>';
|
||||
foreach( $entries as $entry ) {
|
||||
$output .= '<li>';
|
||||
$output .= $entry; // entries are already escaped for output
|
||||
$output .= '</li>';
|
||||
}
|
||||
$output .= '</ul>';
|
||||
}
|
||||
break;
|
||||
}
|
||||
echo $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper to reduce query redundancy due to usage of current_user_can() in get_edit_term_link() etc.
|
||||
*
|
||||
* @param int $term_id
|
||||
* @param string $taxonomy
|
||||
* @return string or null if edit link could not be retrieved
|
||||
*/
|
||||
private static function get_edit_term_link( $term_id, $taxonomy ) {
|
||||
$result = null;
|
||||
$user_id = get_current_user_id();
|
||||
$cached = Groups_Cache::get( self::EDIT_TERM_LINK . '_' . $term_id . '_' . $user_id, self::CACHE_GROUP );
|
||||
if ( $cached !== null ) {
|
||||
$result = $cached->value;
|
||||
unset( $cached );
|
||||
} else {
|
||||
$result = get_edit_term_link( $term_id, $taxonomy );
|
||||
Groups_Cache::set( self::EDIT_TERM_LINK . '_' . $term_id . '_' . $user_id, $result, self::CACHE_GROUP );
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Groups column is sortable.
|
||||
*
|
||||
* Sorting depends on the filters Groups_Admin_Posts::posts_join() and Groups_Admin_Posts::posts_orderby()
|
||||
* which add the relevant group information and sort by group name.
|
||||
*
|
||||
* @see Groups_Admin_Posts::posts_join()
|
||||
* @see Groups_Admin_Posts::posts_orderby()
|
||||
* @param array $sortable_columns
|
||||
* @return array
|
||||
*/
|
||||
public static function manage_edit_post_sortable_columns( $sortable_columns ) {
|
||||
$sortable_columns[self::GROUPS] = self::GROUPS;
|
||||
return $sortable_columns;
|
||||
}
|
||||
|
||||
}
|
||||
Groups_Admin_Post_Columns::init();
|
||||
@@ -0,0 +1,598 @@
|
||||
<?php
|
||||
/**
|
||||
* class-groups-admin-posts.php
|
||||
*
|
||||
* Copyright (c) 2013 "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.4.2
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Additions to post overview admin screens:
|
||||
* - Filter posts by group.
|
||||
* - Apply bulk actions to add or remove group access restrictions.
|
||||
*/
|
||||
class Groups_Admin_Posts {
|
||||
|
||||
/**
|
||||
* Field name
|
||||
* @var string
|
||||
*/
|
||||
const GROUPS_READ = 'groups-read';
|
||||
|
||||
const NOT_RESTRICTED = "#not-restricted#";
|
||||
|
||||
/**
|
||||
* Sets up an admin_init hook where our actions and filters are added.
|
||||
*/
|
||||
public static function init() {
|
||||
add_action( 'admin_init', array( __CLASS__, 'admin_init' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds actions and filters to handle filtering by access restriction
|
||||
* capability.
|
||||
*/
|
||||
public static function admin_init() {
|
||||
if ( current_user_can( GROUPS_ACCESS_GROUPS ) ) {
|
||||
add_action( 'admin_enqueue_scripts', array( __CLASS__, 'admin_enqueue_scripts' ) );
|
||||
add_action( 'admin_head', array( __CLASS__, 'admin_head' ) );
|
||||
add_action( 'restrict_manage_posts', array( __CLASS__, 'restrict_manage_posts' ) );
|
||||
// add_filter( 'parse_query', array( __CLASS__, 'parse_query' ) );
|
||||
|
||||
add_filter( 'posts_where', array( __CLASS__, 'posts_where' ), 10, 2 );
|
||||
add_filter( 'posts_join', array( __CLASS__, 'posts_join' ), 10, 2 );
|
||||
add_filter( 'posts_orderby', array( __CLASS__, 'posts_orderby' ), 10, 2 );
|
||||
|
||||
add_action( 'bulk_edit_custom_box', array( __CLASS__, 'bulk_edit_custom_box' ), 10, 2);
|
||||
add_action( 'save_post', array( __CLASS__, 'save_post' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues the select script.
|
||||
*/
|
||||
public static function admin_enqueue_scripts() {
|
||||
|
||||
global $pagenow;
|
||||
|
||||
if ( $pagenow == 'edit.php' ) {
|
||||
$post_type = isset( $_GET['post_type'] ) ? $_GET['post_type'] : 'post';
|
||||
$post_types_option = Groups_Options::get_option( Groups_Post_Access::POST_TYPES, array() );
|
||||
if ( !isset( $post_types_option[$post_type]['add_meta_box'] ) || $post_types_option[$post_type]['add_meta_box'] ) {
|
||||
Groups_UIE::enqueue( 'select' );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds CSS rules to display our access restriction filter coherently.
|
||||
*/
|
||||
public static function admin_head() {
|
||||
|
||||
global $pagenow;
|
||||
|
||||
if ( $pagenow == 'edit.php' ) {
|
||||
$post_type = isset( $_GET['post_type'] ) ? $_GET['post_type'] : 'post';
|
||||
$post_types_option = Groups_Options::get_option( Groups_Post_Access::POST_TYPES, array() );
|
||||
if ( !isset( $post_types_option[$post_type]['add_meta_box'] ) || $post_types_option[$post_type]['add_meta_box'] ) {
|
||||
echo '<style type="text/css">';
|
||||
echo '.groups-groups-container { display: inline-block; line-height: 24px; padding-bottom: 1em; vertical-align: top; margin-left: 4px; margin-right: 4px; }';
|
||||
echo '.groups-groups-container .groups-select-container { display: inline-block; vertical-align: top; }';
|
||||
echo '.groups-groups-container .groups-select-container select, .groups-bulk-container select.groups-action { float: none; margin-right: 4px; vertical-align: top; }';
|
||||
echo '.groups-groups-container .selectize-control { min-width: 128px; }';
|
||||
echo '.groups-groups-container .selectize-control, .groups-bulk-container select.groups-action { margin-right: 4px; vertical-align: top; }';
|
||||
echo '.groups-groups-container .selectize-input { font-size: inherit; line-height: 18px; padding: 1px 2px 2px 2px; vertical-align: middle; }';
|
||||
echo '.groups-groups-container .selectize-input input[type="text"] { font-size: inherit; vertical-align: middle; }';
|
||||
echo '.groups-groups-container input.button { margin-top: 1px; vertical-align: top; }';
|
||||
echo '.inline-edit-row fieldset .capabilities-bulk-container label span.title { min-width: 5em; padding: 2px 1em; width: auto; }';
|
||||
echo '.tablenav .actions { overflow: visible; }'; // this is important so that the selectize options aren't hidden
|
||||
echo '.wp-list-table td { overflow: visible; }'; // idem for bulk actions
|
||||
echo 'label.groups-read-terms {vertical-align: middle; line-height: 28px; margin-right: 4px; }'; // Terms checkbox
|
||||
echo '</style>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the groups access restriction filter field.
|
||||
*/
|
||||
public static function restrict_manage_posts() {
|
||||
|
||||
global $pagenow, $wpdb;
|
||||
|
||||
if ( is_admin() ) {
|
||||
|
||||
if ( $pagenow == 'edit.php' ) { // check that we're on the right screen
|
||||
|
||||
$post_type = isset( $_GET['post_type'] ) ? $_GET['post_type'] : 'post';
|
||||
$post_types_option = Groups_Options::get_option( Groups_Post_Access::POST_TYPES, array() );
|
||||
|
||||
if ( !isset( $post_types_option[$post_type]['add_meta_box'] ) || $post_types_option[$post_type]['add_meta_box'] ) {
|
||||
|
||||
$output = '';
|
||||
|
||||
// capabilities select
|
||||
$output .= '<div class="groups-groups-container">';
|
||||
$output .= sprintf(
|
||||
'<select class="select group" name="%s[]" multiple="multiple" placeholder="%s" data-placeholder="%s">',
|
||||
esc_attr( Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ ),
|
||||
esc_attr( __( 'Groups …', 'groups' ) ) ,
|
||||
esc_attr( __( 'Groups …', 'groups' ) )
|
||||
);
|
||||
|
||||
$previous_selected = array();
|
||||
if ( !empty( $_GET[Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ] ) ) {
|
||||
$previous_selected = $_GET[Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ];
|
||||
if ( !is_array( $previous_selected ) ) {
|
||||
$previous_selected = array();
|
||||
}
|
||||
}
|
||||
$selected = in_array( self::NOT_RESTRICTED, $previous_selected ) ? ' selected="selected" ' : '';
|
||||
$output .= sprintf( '<option value="%s" %s >%s</option>', self::NOT_RESTRICTED, esc_attr( $selected ), esc_attr( __( '(only unrestricted)', 'groups' ) ) );
|
||||
|
||||
$groups = Groups_Group::get_groups( array( 'order_by' => 'name', 'order' => 'ASC' ) );
|
||||
foreach( $groups as $group ) {
|
||||
$selected = in_array( $group->group_id, $previous_selected ) ? ' selected="selected" ' : '';
|
||||
$output .= sprintf( '<option value="%s" %s >%s</option>', esc_attr( $group->group_id ), esc_attr( $selected ), wp_filter_nohtml_kses( $group->name ) );
|
||||
}
|
||||
$output .= '</select>';
|
||||
$output .= '</div>';
|
||||
$output .= Groups_UIE::render_select( '.select.group' );
|
||||
|
||||
if (
|
||||
function_exists( 'get_term_meta' ) && // >= WordPress 4.4.0 as we query the termmeta table
|
||||
class_exists( 'Groups_Restrict_Categories' ) &&
|
||||
method_exists( 'Groups_Restrict_Categories', 'get_controlled_taxonomies' ) &&
|
||||
method_exists( 'Groups_Restrict_Categories', 'get_term_read_groups' ) // >= Groups Restrict Categories 2.0.0, the method isn't used here but it wouldn't make any sense to query unless we're >= 2.0.0
|
||||
) {
|
||||
$output .= sprintf( '<label class="groups-read-terms" title="%s">', esc_attr( __( 'Also look for groups related to terms', 'groups' ) ) );
|
||||
$output .= sprintf( '<input type="checkbox" name="groups-read-terms" value="1" %s />', empty( $_GET['groups-read-terms'] ) ? '' : ' checked="checked" ' );
|
||||
$output .= __( 'Terms', 'groups' );
|
||||
$output .= '</label>';
|
||||
}
|
||||
echo $output;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk-edit access restriction groups.
|
||||
*
|
||||
* @param string $column_name
|
||||
* @param string $post_type
|
||||
*/
|
||||
public static function bulk_edit_custom_box( $column_name, $post_type ) {
|
||||
|
||||
global $pagenow, $wpdb;
|
||||
|
||||
if ( $column_name == self::GROUPS_READ ) {
|
||||
if ( $pagenow == 'edit.php' ) { // check that we're on the right screen
|
||||
|
||||
$post_type = isset( $_GET['post_type'] ) ? $_GET['post_type'] : 'post';
|
||||
$post_types_option = Groups_Options::get_option( Groups_Post_Access::POST_TYPES, array() );
|
||||
|
||||
if ( !isset( $post_types_option[$post_type]['add_meta_box'] ) || $post_types_option[$post_type]['add_meta_box'] ) {
|
||||
|
||||
$output = '<fieldset class="inline-edit-col-right">';
|
||||
$output .= '<div class="bulk-edit-groups" style="padding:0 0.5em;">';
|
||||
|
||||
// capability/access restriction bulk actions added through extra_tablenav()
|
||||
$output .= '<div id="group-bulk-actions" class="groups-bulk-container" style="display:inline">';
|
||||
|
||||
$output .= '<label style="display:inline;">';
|
||||
$output .= '<span class="title">';
|
||||
$output .= __( 'Groups', 'groups' );
|
||||
$output .= '</span>';
|
||||
$output .= '<select class="groups-action" name="groups-action">';
|
||||
$output .= '<option selected="selected" value="-1">' . __( '— No Change —', 'groups' ) . '</option>';
|
||||
$output .= '<option value="add-group">' . __( 'Add restriction', 'groups' ) . '</option>';
|
||||
$output .= '<option value="remove-group">' . __( 'Remove restriction', 'groups' ) . '</option>';
|
||||
$output .= '</select>';
|
||||
$output .= '</label>';
|
||||
|
||||
$user = new Groups_User( get_current_user_id() );
|
||||
$include = Groups_Access_Meta_Boxes::get_user_can_restrict_group_ids( get_current_user_id() );
|
||||
$groups = Groups_Group::get_groups( array( 'order_by' => 'name', 'order' => 'ASC', 'include' => $include ) );
|
||||
|
||||
$output .= '<div class="groups-groups-container">';
|
||||
$output .= sprintf(
|
||||
'<select class="select bulk-group" name="%s[]" multiple="multiple" placeholder="%s" data-placeholder="%s">',
|
||||
esc_attr( Groups_Post_Access::POSTMETA_PREFIX . 'bulk-' . Groups_Post_Access::READ ),
|
||||
esc_attr( __( 'Choose access restriction groups …', 'groups' ) ) ,
|
||||
esc_attr( __( 'Choose access restriction groups …', 'groups' ) )
|
||||
);
|
||||
|
||||
foreach( $groups as $group ) {
|
||||
$output .= sprintf( '<option value="%s" >%s</option>', esc_attr( $group->group_id ), wp_filter_nohtml_kses( $group->name ) );
|
||||
}
|
||||
$output .= '</select>';
|
||||
$output .= '</div>'; // .groups-groups-container
|
||||
$output .= Groups_UIE::render_select( '.select.bulk-group' );
|
||||
|
||||
$output .= '</div>'; // .groups-bulk-container
|
||||
|
||||
$output .= '</div>'; // .bulk-edit-groups
|
||||
$output .= '</fieldset>'; // .inline-edit-col-right
|
||||
|
||||
$output .= wp_nonce_field( 'post-group', 'bulk-post-group-nonce', true, false );
|
||||
|
||||
echo $output;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles access restriction group modifications from bulk-editing.
|
||||
* This is called once for each post that is included in bulk-editing.
|
||||
* The fields that are handled here are rendered through the
|
||||
* bulk_edit_custom_box() method in this class.
|
||||
*
|
||||
* @param int $post_id
|
||||
*/
|
||||
public static function save_post( $post_id ) {
|
||||
if ( isset( $_REQUEST['groups-action'] ) ) {
|
||||
if ( wp_verify_nonce( $_REQUEST['bulk-post-group-nonce'], 'post-group' ) ) {
|
||||
$field = Groups_Post_Access::POSTMETA_PREFIX . 'bulk-' . Groups_Post_Access::READ;
|
||||
if ( !empty( $_REQUEST[$field] ) && is_array( $_REQUEST[$field] ) ) {
|
||||
if ( Groups_Access_Meta_Boxes::user_can_restrict() ) {
|
||||
$include = Groups_Access_Meta_Boxes::get_user_can_restrict_group_ids();
|
||||
$groups = Groups_Group::get_groups( array( 'order_by' => 'name', 'order' => 'ASC', 'include' => $include ) );
|
||||
$group_ids = array();
|
||||
foreach( $groups as $group ) {
|
||||
$group_ids[] = $group->group_id;
|
||||
}
|
||||
foreach( $_REQUEST[$field] as $group_id ) {
|
||||
if ( $group = Groups_Group::read( $group_id ) ) {
|
||||
if ( in_array( $group->group_id, $group_ids ) ) {
|
||||
switch( $_REQUEST['groups-action'] ) {
|
||||
case 'add-group' :
|
||||
Groups_Post_Access::create( array(
|
||||
'post_id' => $post_id,
|
||||
'group_id' => $group->group_id
|
||||
) );
|
||||
break;
|
||||
case 'remove-group' :
|
||||
Groups_Post_Access::delete( $post_id, array( 'groups_read' => $group->group_id ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Query modifier to take the selected access restriction groups into
|
||||
* account.
|
||||
*
|
||||
* @param WP_Query $query query object passed by reference
|
||||
*/
|
||||
public static function parse_query( &$query ) {
|
||||
|
||||
global $pagenow;
|
||||
|
||||
if ( is_admin() ) {
|
||||
|
||||
if ( $pagenow == 'edit.php' ) { // check that we're on the right screen
|
||||
|
||||
$post_type = isset( $_GET['post_type'] ) ? $_GET['post_type'] : 'post';
|
||||
$post_types_option = Groups_Options::get_option( Groups_Post_Access::POST_TYPES, array() );
|
||||
|
||||
if ( !isset( $post_types_option[$post_type]['add_meta_box'] ) || $post_types_option[$post_type]['add_meta_box'] ) {
|
||||
|
||||
if ( !empty( $_GET[Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ] ) &&
|
||||
is_array( $_GET[Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ] )
|
||||
) {
|
||||
|
||||
$include_unrestricted = false;
|
||||
if ( in_array( self::NOT_RESTRICTED, $_GET[Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ] ) ) {
|
||||
$include_unrestricted = true;
|
||||
}
|
||||
|
||||
$group_ids = array();
|
||||
foreach ( $_GET[Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ] as $group_id ) {
|
||||
if ( Groups_Group::read( $group_id ) ) {
|
||||
$group_ids[] = $group_id;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !empty( $group_ids ) ) {
|
||||
if ( $include_unrestricted ) {
|
||||
// meta_query does not handle a conjunction
|
||||
// on the same meta field correctly
|
||||
// (at least not up to WordPress 3.7.1)
|
||||
// $query->query_vars['meta_query'] = array (
|
||||
// 'relation' => 'OR',
|
||||
// array (
|
||||
// 'key' => Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ,
|
||||
// 'value' => $group_ids,
|
||||
// 'compare' => 'IN'
|
||||
// ),
|
||||
// array (
|
||||
// 'key' => Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ,
|
||||
// 'compare' => 'NOT EXISTS'
|
||||
// )
|
||||
// );
|
||||
// we'll limit it to show just unrestricted entries
|
||||
// until the above is solved
|
||||
$query->query_vars['meta_query'] = array (
|
||||
array (
|
||||
'key' => Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ,
|
||||
'compare' => 'NOT EXISTS'
|
||||
)
|
||||
);
|
||||
} else {
|
||||
$query->query_vars['meta_query'] = array (
|
||||
array (
|
||||
'key' => Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ,
|
||||
'value' => $group_ids,
|
||||
'compare' => 'IN'
|
||||
)
|
||||
);
|
||||
}
|
||||
} else if ( $include_unrestricted ) {
|
||||
$query->query_vars['meta_query'] = array (
|
||||
array (
|
||||
'key' => Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ,
|
||||
'compare' => 'NOT EXISTS'
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters out posts by group. This is used when you choose groups on the post admin screen so that
|
||||
* only those posts who are restricted by groups are shown.
|
||||
*
|
||||
* @param string $where
|
||||
* @param WP_Query $query
|
||||
* @return string
|
||||
*/
|
||||
public static function posts_where( $where, $query ) {
|
||||
|
||||
global $wpdb;
|
||||
|
||||
if ( self::extend_for_filter_groups_read( $query ) ) {
|
||||
|
||||
$group_ids = array();
|
||||
foreach ( $_GET[Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ] as $group_id ) {
|
||||
if ( $group_id = Groups_Utility::id( $group_id ) ) {
|
||||
if ( Groups_Group::read( $group_id ) ) {
|
||||
$group_ids[] = $group_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !empty( $group_ids ) ) {
|
||||
$groups = ' ( ' . implode(',', $group_ids ) . ' ) ';
|
||||
$group_table = _groups_get_tablename( 'group' );
|
||||
if (
|
||||
!empty( $_GET['groups-read-terms'] ) &&
|
||||
function_exists( 'get_term_meta' ) && // >= WordPress 4.4.0 as we query the termmeta table
|
||||
class_exists( 'Groups_Restrict_Categories' ) &&
|
||||
method_exists( 'Groups_Restrict_Categories', 'get_controlled_taxonomies' ) &&
|
||||
method_exists( 'Groups_Restrict_Categories', 'get_term_read_groups' ) // >= Groups Restrict Categories 2.0.0, the method isn't used here but it wouldn't make any sense to query unless we're >= 2.0.0
|
||||
) {
|
||||
$where .= "
|
||||
AND $wpdb->posts.ID IN (
|
||||
SELECT post_id
|
||||
FROM $wpdb->postmeta pm
|
||||
WHERE
|
||||
pm.meta_key = 'groups-read' AND
|
||||
pm.meta_value IN $groups
|
||||
UNION ALL
|
||||
SELECT p.ID post_id
|
||||
FROM $wpdb->posts p
|
||||
LEFT JOIN $wpdb->term_relationships tr ON p.ID = tr.object_id
|
||||
LEFT JOIN $wpdb->term_taxonomy tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
|
||||
LEFT JOIN $wpdb->termmeta tm ON tt.term_id = tm.term_id
|
||||
WHERE
|
||||
tm.meta_key = 'groups-read' AND
|
||||
tm.meta_value IN $groups
|
||||
)
|
||||
";
|
||||
} else {
|
||||
$where .= "
|
||||
AND $wpdb->posts.ID IN (
|
||||
SELECT post_id
|
||||
FROM $wpdb->postmeta pm
|
||||
WHERE
|
||||
pm.meta_key = 'groups-read' AND
|
||||
pm.meta_value IN $groups
|
||||
)
|
||||
";
|
||||
}
|
||||
} // !empty( $group_ids )
|
||||
|
||||
}
|
||||
|
||||
return $where;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds to the join to allow advanced sorting by group on the admin back end for post tables.
|
||||
*
|
||||
* @param string $join
|
||||
* @param WP_Query $query
|
||||
*/
|
||||
public static function posts_join( $join, $query ) {
|
||||
global $wpdb;
|
||||
if ( self::extend_for_orderby_groups_read( $query ) ) {
|
||||
$group_table = _groups_get_tablename( 'group' );
|
||||
if ( function_exists( 'get_term_meta' ) ) { // >= WordPress 4.4.0 as we query the termmeta table
|
||||
$join .= "
|
||||
LEFT JOIN (
|
||||
SELECT p.ID post_id, GROUP_CONCAT(DISTINCT groups_read.group_name ORDER BY groups_read.group_name) groups
|
||||
FROM $wpdb->posts p
|
||||
LEFT JOIN (
|
||||
SELECT post_id, g.name group_name
|
||||
FROM $wpdb->postmeta pm
|
||||
LEFT JOIN $group_table g ON pm.meta_value = g.group_id
|
||||
WHERE pm.meta_key = 'groups-read'
|
||||
UNION ALL
|
||||
SELECT p.ID post_id, g.name group_name
|
||||
FROM $wpdb->posts p
|
||||
LEFT JOIN $wpdb->term_relationships tr ON p.ID = tr.object_id
|
||||
LEFT JOIN $wpdb->term_taxonomy tt ON tr.term_taxonomy_id = tt.term_taxonomy_id
|
||||
LEFT JOIN $wpdb->termmeta tm ON tt.term_id = tm.term_id
|
||||
LEFT JOIN $group_table g ON tm.meta_value = g.group_id
|
||||
WHERE tm.meta_key = 'groups-read'
|
||||
) as groups_read ON p.ID = groups_read.post_id
|
||||
GROUP BY p.ID
|
||||
) groups_tmp ON $wpdb->posts.ID = groups_tmp.post_id
|
||||
";
|
||||
} else {
|
||||
$join .= "
|
||||
LEFT JOIN (
|
||||
SELECT p.ID post_id, GROUP_CONCAT(DISTINCT groups_read.group_name ORDER BY groups_read.group_name) groups
|
||||
FROM $wpdb->posts p
|
||||
LEFT JOIN (
|
||||
SELECT post_id, g.name group_name
|
||||
FROM $wpdb->postmeta pm
|
||||
LEFT JOIN $group_table g ON pm.meta_value = g.group_id
|
||||
WHERE pm.meta_key = 'groups-read'
|
||||
) as groups_read ON p.ID = groups_read.post_id
|
||||
GROUP BY p.ID
|
||||
) groups_tmp ON $wpdb->posts.ID = groups_tmp.post_id
|
||||
";
|
||||
}
|
||||
}
|
||||
return $join;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extend the orderby clause to sort by groups related to the post and its terms.
|
||||
*
|
||||
* @param $string $orderby
|
||||
* @param WP_Query $query
|
||||
* @return string
|
||||
*/
|
||||
public static function posts_orderby( $orderby, $query ) {
|
||||
if ( self::extend_for_orderby_groups_read( $query ) ) {
|
||||
switch( $query->get( 'order' ) ) {
|
||||
case 'desc' :
|
||||
case 'DESC' :
|
||||
$order = 'DESC';
|
||||
break;
|
||||
default :
|
||||
$order = 'ASC';
|
||||
}
|
||||
$prefix = ' groups_tmp.groups ' . $order;
|
||||
if ( !empty( $orderby ) ) {
|
||||
$prefix .= ' , ';
|
||||
}
|
||||
$orderby = $prefix . $orderby;
|
||||
}
|
||||
return $orderby;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we should apply our posts_join and posts_orderby filters. Used in those.
|
||||
*
|
||||
* @param WP_Query $query
|
||||
* @return boolean
|
||||
*/
|
||||
private static function extend_for_orderby_groups_read( &$query ) {
|
||||
$result = false;
|
||||
if ( is_admin() ) {
|
||||
// check if query is for a post type we handle
|
||||
$post_types = $query->get( 'post_type' );
|
||||
if ( !is_array( $post_types ) ) {
|
||||
$post_types = array( $post_types );
|
||||
}
|
||||
foreach( $post_types as $post_type ) {
|
||||
$post_types_option = Groups_Options::get_option( Groups_Post_Access::POST_TYPES, array() );
|
||||
if (
|
||||
!isset( $post_types_option[$post_type]['add_meta_box'] ) ||
|
||||
$post_types_option[$post_type]['add_meta_box']
|
||||
) {
|
||||
// only act on post etc. screens
|
||||
$screen = get_current_screen();
|
||||
if (
|
||||
!empty( $screen ) &&
|
||||
!empty( $screen->id ) &&
|
||||
( $screen->id == 'edit-' . $post_type )
|
||||
) {
|
||||
if ( $query->get( 'orderby' ) == self::GROUPS_READ ) {
|
||||
$result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we should apply our posts_where filter. Used in it.
|
||||
*
|
||||
* @param WP_Query $query
|
||||
* @return boolean
|
||||
*/
|
||||
private static function extend_for_filter_groups_read( &$query ) {
|
||||
$result = false;
|
||||
if ( is_admin() ) {
|
||||
// check if query is for a post type we handle
|
||||
$post_types = $query->get( 'post_type' );
|
||||
$post_types_option = Groups_Options::get_option( Groups_Post_Access::POST_TYPES, array() );
|
||||
if ( !is_array( $post_types ) ) {
|
||||
$post_types = array( $post_types );
|
||||
}
|
||||
foreach( $post_types as $post_type ) {
|
||||
if (
|
||||
!isset( $post_types_option[$post_type]['add_meta_box'] ) ||
|
||||
$post_types_option[$post_type]['add_meta_box']
|
||||
) {
|
||||
// only act on post etc. screens
|
||||
$screen = get_current_screen();
|
||||
if (
|
||||
!empty( $screen ) &&
|
||||
!empty( $screen->id ) &&
|
||||
( $screen->id == 'edit-' . $post_type )
|
||||
) {
|
||||
if (
|
||||
!empty( $_GET[Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ] ) &&
|
||||
is_array( $_GET[Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ] )
|
||||
) {
|
||||
$result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
Groups_Admin_Posts::init();
|
||||
@@ -0,0 +1,235 @@
|
||||
<?php
|
||||
/**
|
||||
* class-groups-admin-user-profile.php
|
||||
*
|
||||
* Copyright (c) 2013 "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.3.11
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show group info on user profile pages and let admins edit group membership.
|
||||
*/
|
||||
class Groups_Admin_User_Profile {
|
||||
|
||||
/**
|
||||
* Adds user profile actions.
|
||||
*/
|
||||
public static function init() {
|
||||
add_action( 'user_new_form', array( __CLASS__, 'user_new_form' ) );
|
||||
add_action( 'user_register', array( __CLASS__, 'user_register' ) );
|
||||
add_action( 'show_user_profile', array( __CLASS__, 'show_user_profile' ) );
|
||||
add_action( 'edit_user_profile', array( __CLASS__, 'edit_user_profile' ) );
|
||||
add_action( 'personal_options_update', array( __CLASS__, 'personal_options_update' ) );
|
||||
add_action( 'edit_user_profile_update', array( __CLASS__, 'edit_user_profile_update' ) );
|
||||
add_action( 'admin_enqueue_scripts', array( __CLASS__, 'admin_enqueue_scripts' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues the select script on the user-edit and profile screens.
|
||||
*/
|
||||
public static function admin_enqueue_scripts() {
|
||||
$screen = get_current_screen();
|
||||
if ( isset( $screen->id ) ) {
|
||||
switch( $screen->id ) {
|
||||
case 'user' : // creating a new user
|
||||
case 'user-edit' :
|
||||
case 'profile' :
|
||||
require_once GROUPS_VIEWS_LIB . '/class-groups-uie.php';
|
||||
Groups_UIE::enqueue( 'select' );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook for the form to create a new user.
|
||||
*
|
||||
* See wp-admin/user-new.php
|
||||
*
|
||||
* @param string $type form context, expecting 'add-existing-user' (Multisite), or 'add-new-user' (single site and network admin)
|
||||
*/
|
||||
public static function user_new_form( $type = null ) {
|
||||
global $wpdb;
|
||||
if ( $type == 'add-new-user' ) {
|
||||
if ( current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
|
||||
$output = '<h3>' . __( 'Groups', 'groups' ) . '</h3>';
|
||||
$groups_table = _groups_get_tablename( 'group' );
|
||||
if ( $groups = $wpdb->get_results( "SELECT * FROM $groups_table ORDER BY name" ) ) {
|
||||
$output .= '<style type="text/css">';
|
||||
$output .= '.groups .selectize-input { font-size: inherit; }';
|
||||
$output .= '</style>';
|
||||
$output .= sprintf(
|
||||
'<select id="user-groups" class="groups" name="group_ids[]" multiple="multiple" placeholder="%s" data-placeholder="%s">',
|
||||
esc_attr( __( 'Choose groups …', 'groups' ) ) ,
|
||||
esc_attr( __( 'Choose groups …', 'groups' ) )
|
||||
);
|
||||
foreach( $groups as $group ) {
|
||||
$output .= sprintf( '<option value="%d">%s</option>', Groups_Utility::id( $group->group_id ), wp_filter_nohtml_kses( $group->name ) );
|
||||
}
|
||||
$output .= '</select>';
|
||||
$output .= Groups_UIE::render_select( '#user-groups' );
|
||||
$output .= '<p class="description">' . __( 'The user is a member of the chosen groups.', 'groups' ) . '</p>';
|
||||
}
|
||||
echo $output;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the new user to chosen groups when creating a new user account
|
||||
* from the admin side.
|
||||
*
|
||||
* @param int $user_id
|
||||
*/
|
||||
public static function user_register( $user_id ) {
|
||||
|
||||
global $wpdb;
|
||||
|
||||
if ( is_admin() ) {
|
||||
if ( function_exists( 'get_current_screen' ) ) {
|
||||
$screen = get_current_screen();
|
||||
if ( isset( $screen->id ) && $screen->id === 'user' ) {
|
||||
if ( current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
|
||||
$groups_table = _groups_get_tablename( 'group' );
|
||||
if ( $groups = $wpdb->get_results( "SELECT * FROM $groups_table" ) ) {
|
||||
$user_group_ids = isset( $_POST['group_ids'] ) && is_array( $_POST['group_ids'] ) ? $_POST['group_ids'] : array();
|
||||
foreach( $groups as $group ) {
|
||||
if ( in_array( $group->group_id, $user_group_ids ) ) {
|
||||
if ( !Groups_User_Group::read( $user_id, $group->group_id ) ) {
|
||||
Groups_User_Group::create( array( 'user_id' => $user_id, 'group_id' => $group->group_id ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Own profile.
|
||||
* @param WP_User $user
|
||||
*/
|
||||
public static function show_user_profile( $user ) {
|
||||
if ( current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
|
||||
self::edit_user_profile( $user );
|
||||
} else {
|
||||
$output = '<h3>' . __( 'Groups', 'groups' ) . '</h3>';
|
||||
$user = new Groups_User( $user->ID );
|
||||
$groups = $user->groups;
|
||||
if ( is_array( $groups ) ) {
|
||||
if ( count( $groups ) > 0 ) {
|
||||
usort( $groups, array( __CLASS__, 'by_group_name' ) );
|
||||
$output .= '<ul>';
|
||||
foreach( $groups as $group ) {
|
||||
$output .= '<li>' . wp_filter_nohtml_kses( $group->name ) . '</li>';
|
||||
}
|
||||
$output .= '</ul>';
|
||||
}
|
||||
}
|
||||
echo $output;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Editing a user profile.
|
||||
* @param WP_User $user
|
||||
*/
|
||||
public static function edit_user_profile( $user ) {
|
||||
global $wpdb;
|
||||
if ( current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
|
||||
$output = '<h3>' . __( 'Groups', 'groups' ) . '</h3>';
|
||||
$user = new Groups_User( $user->ID );
|
||||
$user_groups = $user->groups;
|
||||
$groups_table = _groups_get_tablename( 'group' );
|
||||
if ( $groups = $wpdb->get_results( "SELECT * FROM $groups_table ORDER BY name" ) ) {
|
||||
$output .= '<style type="text/css">';
|
||||
$output .= '.groups .selectize-input { font-size: inherit; }';
|
||||
$output .= '</style>';
|
||||
$output .= sprintf(
|
||||
'<select id="user-groups" class="groups" name="group_ids[]" multiple="multiple" placeholder="%s" data-placeholder="%s">',
|
||||
esc_attr( __( 'Choose groups …', 'groups' ) ) ,
|
||||
esc_attr( __( 'Choose groups …', 'groups' ) )
|
||||
);
|
||||
foreach( $groups as $group ) {
|
||||
$is_member = Groups_User_Group::read( $user->ID, $group->group_id ) ? true : false;
|
||||
$output .= sprintf( '<option value="%d" %s>%s</option>', Groups_Utility::id( $group->group_id ), $is_member ? ' selected="selected" ' : '', wp_filter_nohtml_kses( $group->name ) );
|
||||
}
|
||||
$output .= '</select>';
|
||||
$output .= Groups_UIE::render_select( '#user-groups' );
|
||||
$output .= '<p class="description">' . __( 'The user is a member of the chosen groups.', 'groups' ) . '</p>';
|
||||
}
|
||||
echo $output;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the group membership when a user's own profile is saved - but
|
||||
* for group admins on their own profile page only.
|
||||
*
|
||||
* @param int $user_id
|
||||
* @see Groups_Admin_User_Profile::edit_user_profile_update()
|
||||
*/
|
||||
public static function personal_options_update( $user_id ) {
|
||||
// We're using the same method as for editing another user's profile,
|
||||
// but let's check for group admin here as well.
|
||||
if ( current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
|
||||
self::edit_user_profile_update( $user_id );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the group membership.
|
||||
* @param int $user_id
|
||||
*/
|
||||
public static function edit_user_profile_update( $user_id ) {
|
||||
global $wpdb;
|
||||
if ( current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
|
||||
$groups_table = _groups_get_tablename( 'group' );
|
||||
if ( $groups = $wpdb->get_results( "SELECT * FROM $groups_table" ) ) {
|
||||
$user_group_ids = isset( $_POST['group_ids'] ) && is_array( $_POST['group_ids'] ) ? $_POST['group_ids'] : array();
|
||||
foreach( $groups as $group ) {
|
||||
if ( in_array( $group->group_id, $user_group_ids ) ) {
|
||||
if ( !Groups_User_Group::read( $user_id, $group->group_id ) ) {
|
||||
Groups_User_Group::create( array( 'user_id' => $user_id, 'group_id' => $group->group_id ) );
|
||||
}
|
||||
} else {
|
||||
if ( Groups_User_Group::read( $user_id, $group->group_id ) ) {
|
||||
Groups_User_Group::delete( $user_id, $group->group_id );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* usort helper
|
||||
* @param Groups_Group $o1
|
||||
* @param Groups_Group $o2
|
||||
* @return int strcmp result for group names
|
||||
*/
|
||||
public static function by_group_name( $o1, $o2 ) {
|
||||
return strcmp( $o1->name, $o2->name );
|
||||
}
|
||||
|
||||
}
|
||||
Groups_Admin_User_Profile::init();
|
||||
@@ -0,0 +1,395 @@
|
||||
<?php
|
||||
/**
|
||||
* class-groups-admin-users.php
|
||||
*
|
||||
* Copyright (c) 2012 "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.0.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Users admin integration with Groups.
|
||||
*/
|
||||
class Groups_Admin_Users {
|
||||
|
||||
const GROUPS = 'groups_user_groups';
|
||||
|
||||
/**
|
||||
* Hooks into filters to add the Groups column to the users table.
|
||||
*/
|
||||
public static function init() {
|
||||
// we hook this on admin_init so that current_user_can() is available
|
||||
add_action( 'admin_init', array( __CLASS__, 'setup' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the filters and actions only for users who have the right
|
||||
* Groups permissions.
|
||||
*/
|
||||
public static function setup() {
|
||||
if ( current_user_can( GROUPS_ACCESS_GROUPS ) ) {
|
||||
// filters to display the user's groups
|
||||
add_filter( 'manage_users_columns', array( __CLASS__, 'manage_users_columns' ) );
|
||||
// args: unknown, string $column_name, int $user_id
|
||||
add_filter( 'manage_users_custom_column', array( __CLASS__, 'manage_users_custom_column' ), 10, 3 );
|
||||
}
|
||||
if ( current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
|
||||
if ( !is_network_admin() ) {
|
||||
// scripts
|
||||
add_action( 'admin_enqueue_scripts', array( __CLASS__, 'admin_enqueue_scripts' ) );
|
||||
// styles
|
||||
add_action( 'admin_head', array( __CLASS__, 'admin_head' ) );
|
||||
// allow to add or remove selected users to groups
|
||||
add_action( 'load-users.php', array( __CLASS__, 'load_users' ) );
|
||||
// add links to filter users by group
|
||||
add_filter( 'views_users', array( __CLASS__, 'views_users' ) );
|
||||
// modify query to filter users by group
|
||||
add_filter( 'pre_user_query', array( __CLASS__, 'pre_user_query' ) );
|
||||
// WP_Users_List_Table implements extra_tablenav() where the restrict_manage_users action is invoked.
|
||||
// As the extra_tablenav() method does not define a generic extension point, this is
|
||||
// the best shot we get at inserting our group actions block (currently we're at WordPress 3.6.1).
|
||||
// We choose to use our own group-actions block instead of re-using the existing bulk-actions,
|
||||
// to have a more explicit user interface which makes it clear that these actions
|
||||
// are directed at relating users and groups.
|
||||
add_action( 'restrict_manage_users', array( __CLASS__, 'restrict_manage_users' ), 0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Modify query to filter users by group.
|
||||
*
|
||||
* @param WP_User_Query $user_query
|
||||
* @return WP_User_Query
|
||||
*/
|
||||
public static function pre_user_query( $user_query ) {
|
||||
global $pagenow, $wpdb;
|
||||
if ( ( $pagenow == 'users.php' ) && empty( $_GET['page'] ) ) {
|
||||
if ( isset( $_REQUEST['group_ids'] ) && is_array( $_REQUEST['group_ids'] ) ) {
|
||||
$group_ids = array_map( array( 'Groups_Utility', 'id' ), array_map( 'trim', $_REQUEST['group_ids'] ) );
|
||||
$include = array();
|
||||
foreach ( $group_ids as $group_id ) {
|
||||
if ( Groups_Group::read( $group_id ) ) {
|
||||
$group = new Groups_Group( $group_id );
|
||||
$users = $group->users;
|
||||
if ( count( $users ) > 0 ) {
|
||||
foreach( $users as $user ) {
|
||||
$include[] = $user->user->ID;
|
||||
}
|
||||
} else { // no results
|
||||
$include[] = 0;
|
||||
}
|
||||
unset( $group );
|
||||
unset( $users );
|
||||
}
|
||||
}
|
||||
if ( count( $include ) > 0 ) {
|
||||
$include = array_unique( $include );
|
||||
$ids = implode( ',', wp_parse_id_list( $include ) );
|
||||
$user_query->query_where .= " AND $wpdb->users.ID IN ($ids)";
|
||||
}
|
||||
}
|
||||
}
|
||||
return $user_query;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue scripts the group-actions.
|
||||
*/
|
||||
public static function admin_enqueue_scripts() {
|
||||
|
||||
global $pagenow;
|
||||
|
||||
if ( ( $pagenow == 'users.php' ) && empty( $_GET['page'] ) ) {
|
||||
Groups_UIE::enqueue( 'select' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the group add/remove buttons after the last action box.
|
||||
*/
|
||||
public static function admin_head() {
|
||||
|
||||
global $pagenow;
|
||||
|
||||
if ( ( $pagenow == 'users.php' ) && empty( $_GET['page'] ) ) {
|
||||
|
||||
// .subsubsub rule added because with views_users() the list can get long
|
||||
// icon distinguishes from role links
|
||||
echo '<style type="text/css">';
|
||||
echo '.subsubsub { white-space: normal; }';
|
||||
echo 'a.group { background: url(' . GROUPS_PLUGIN_URL . '/images/groups-grey-8x8.png) transparent no-repeat left center; padding-left: 10px;}';
|
||||
echo '</style>';
|
||||
|
||||
// group-actions
|
||||
echo '<style type="text/css">';
|
||||
echo '.groups-bulk-container { display: inline-block; line-height: 24px; padding-bottom: 2px; vertical-align: top; margin-left: 0.31em; margin-right: 0.31em; }';
|
||||
echo '.groups-bulk-container .groups-select-container { display: inline-block; vertical-align: top; }';
|
||||
echo '.groups-bulk-container .groups-select-container select, .groups-bulk-container select.groups-action { float: none; margin-right: 4px; vertical-align: top; }';
|
||||
echo '.groups-bulk-container .selectize-control { min-width: 128px; }';
|
||||
echo '.groups-bulk-container .selectize-control, .groups-bulk-container select.groups-action { margin-right: 4px; vertical-align: top; }';
|
||||
echo '.groups-bulk-container .selectize-input { font-size: inherit; line-height: 18px; padding: 1px 2px 2px 2px; vertical-align: middle; }';
|
||||
echo '.groups-bulk-container .selectize-input input[type="text"] { font-size: inherit; vertical-align: middle; height: 24px; }';
|
||||
echo '.groups-bulk-container input.button { margin-top: 1px; vertical-align: top; }';
|
||||
echo '.tablenav .actions { overflow: visible; }'; // this is important so that the selectize options aren't hidden
|
||||
echo '</style>';
|
||||
|
||||
// groups filter
|
||||
echo '<style type="text/css">';
|
||||
echo '.groups-filter-container { display: inline-block; line-height: 24px; vertical-align: middle; }';
|
||||
echo '.groups-filter-container .groups-select-container { display: inline-block; vertical-align: top; }';
|
||||
echo '.groups-filter-container .groups-select-container select, .groups-bulk-container select.groups-action { float: none; margin-right: 4px; vertical-align: top; }';
|
||||
echo '.groups-filter-container .selectize-control { min-width: 128px; }';
|
||||
echo '.groups-filter-container .selectize-control, .groups-bulk-container select.groups-action { margin-right: 4px; vertical-align: top; }';
|
||||
echo '.groups-filter-container .selectize-input { font-size: inherit; line-height: 18px; padding: 1px 2px 2px 2px; vertical-align: middle; }';
|
||||
echo '.groups-filter-container .selectize-input input[type="text"] { font-size: inherit; vertical-align: middle; height: 24px; }';
|
||||
echo '.groups-filter-container .selectize-input .item a { line-height: inherit; }'; // neutralize .subsubsub a rule
|
||||
echo '.groups-filter-container input.button { margin-top: 1px; vertical-align: top; }';
|
||||
echo '</style>';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders group actions in the users table's extra_tablenav().
|
||||
*/
|
||||
public static function restrict_manage_users() {
|
||||
|
||||
global $pagenow, $wpdb, $groups_select_user_groups_index;
|
||||
|
||||
// We don't handle multiple instances so don't render another.
|
||||
if ( !isset( $groups_select_user_groups_index ) ) {
|
||||
$groups_select_user_groups_index = 0;
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
|
||||
$output = '';
|
||||
|
||||
if ( ( $pagenow == 'users.php' ) && empty( $_GET['page'] ) ) {
|
||||
// groups select
|
||||
$groups_table = _groups_get_tablename( 'group' );
|
||||
if ( $groups = $wpdb->get_results( "SELECT * FROM $groups_table ORDER BY name" ) ) {
|
||||
$groups_select = sprintf(
|
||||
'<select id="user-groups" class="groups" name="group_ids[]" multiple="multiple" placeholder="%s" data-placeholder="%s">',
|
||||
esc_attr( __( 'Choose groups …', 'groups' ) ) ,
|
||||
esc_attr( __( 'Choose groups …', 'groups' ) )
|
||||
);
|
||||
foreach( $groups as $group ) {
|
||||
$is_member = false;
|
||||
$groups_select .= sprintf(
|
||||
'<option value="%d" %s>%s</option>',
|
||||
Groups_Utility::id( $group->group_id ),
|
||||
$is_member ? ' selected="selected" ' : '',
|
||||
wp_filter_nohtml_kses( $group->name )
|
||||
);
|
||||
}
|
||||
$groups_select .= '</select>';
|
||||
}
|
||||
|
||||
// group bulk actions added through extra_tablenav()
|
||||
$box = '<div id="group-bulk-actions" class="groups-bulk-container">';
|
||||
$box .= '<div class="groups-select-container">';
|
||||
$box .= $groups_select;
|
||||
$box .= '</div>';
|
||||
$box .= '<select class="groups-action" name="groups-action">';
|
||||
$box .= '<option selected="selected" value="-1">' . __( 'Group Actions', 'groups' ) . '</option>';
|
||||
$box .= '<option value="add-group">' . __( 'Add to group', 'groups' ) . '</option>';
|
||||
$box .= '<option value="remove-group">' . __( 'Remove from group', 'groups' ) . '</option>';
|
||||
$box .= '</select>';
|
||||
$box .= sprintf( '<input class="button" type="submit" name="groups" value="%s" />', __( 'Apply', 'groups' ) );
|
||||
$box .= '</div>';
|
||||
$box = str_replace( '"', "'", $box );
|
||||
|
||||
$nonce = wp_nonce_field( 'user-group', 'bulk-user-group-nonce', true, false );
|
||||
$nonce = str_replace( '"', "'", $nonce );
|
||||
$box .= $nonce;
|
||||
|
||||
$box .= '<script type="text/javascript">';
|
||||
$box .= 'if ( typeof jQuery !== "undefined" ) {';
|
||||
$box .= 'jQuery("document").ready(function(){';
|
||||
$box .= 'jQuery(".tablenav.top .alignleft.actions:last").after("<div id=\"groups-bulk-actions-block\" class=\"alignleft actions\"></div>");';
|
||||
$box .= 'jQuery("#group-bulk-actions").appendTo(jQuery("#groups-bulk-actions-block"));';
|
||||
$box .= '});';
|
||||
$box .= '}';
|
||||
$box .= '</script>';
|
||||
|
||||
$output .= $box;
|
||||
$output .= Groups_UIE::render_select( '#user-groups' );
|
||||
}
|
||||
echo $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooked on filter in class-wp-list-table.php to
|
||||
* filter by group.
|
||||
* @param array $views
|
||||
*/
|
||||
public static function views_users( $views ) {
|
||||
global $pagenow, $wpdb;
|
||||
if ( ( $pagenow == 'users.php' ) && empty( $_GET['page'] ) ) {
|
||||
$output = '<form id="filter-groups-form" action="" method="get">';
|
||||
$output .= '<div class="groups-filter-container">';
|
||||
$output .= '<div class="groups-select-container">';
|
||||
$output .= sprintf(
|
||||
'<select id="filter-groups" class="groups" name="group_ids[]" multiple="multiple" placeholder="%s" data-placeholder="%s">',
|
||||
esc_attr( __( 'Choose groups …', 'groups' ) ) ,
|
||||
esc_attr( __( 'Choose groups …', 'groups' ) )
|
||||
);
|
||||
$user_group_table = _groups_get_tablename( 'user_group' );
|
||||
$groups = Groups_Group::get_groups( array( 'order_by' => 'name', 'order' => 'ASC' ) );
|
||||
$user_counts = array();
|
||||
$counts = $wpdb->get_results( "SELECT COUNT(user_id) AS count, group_id FROM $user_group_table GROUP BY group_id" );
|
||||
if ( !empty( $counts ) && is_array( $counts ) ) {
|
||||
foreach( $counts as $count ) {
|
||||
$user_counts[$count->group_id] = $count->count;
|
||||
}
|
||||
}
|
||||
foreach( $groups as $group ) {
|
||||
// Do not use $user_count = count( $group->users ); here,
|
||||
// as it creates a lot of unneccessary objects and can lead
|
||||
// to out of memory issues on large user bases.
|
||||
$user_count = isset( $user_counts[$group->group_id] ) ? $user_counts[$group->group_id] : 0;
|
||||
$selected = isset( $_REQUEST['group_ids'] ) && is_array( $_REQUEST['group_ids'] ) && in_array( $group->group_id, $_REQUEST['group_ids'] );
|
||||
$output .= sprintf(
|
||||
'<option value="%d" %s>%s</option>',
|
||||
Groups_Utility::id( $group->group_id ),
|
||||
$selected ? ' selected="selected" ' : '',
|
||||
sprintf( '%s <span class="count">(%s)</span>', wp_filter_nohtml_kses( $group->name ), esc_html( $user_count ) )
|
||||
);
|
||||
}
|
||||
$output .= '</select>';
|
||||
$output .= '</div>'; // .groups-select-container
|
||||
$output .= '</div>'; // .groups-filter-container
|
||||
$output .= '<input class="button" type="submit" value="' . esc_attr( __( 'Filter', 'groups' ) ) . '"/>';
|
||||
$output .= '</form>';
|
||||
$output .= Groups_UIE::render_select( '#filter-groups' );
|
||||
$views['groups'] = $output;
|
||||
}
|
||||
return $views;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds or removes users to/from groups.
|
||||
*/
|
||||
public static function load_users() {
|
||||
if ( current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
|
||||
$users = isset( $_REQUEST['users'] ) ? $_REQUEST['users'] : null;
|
||||
$action = null;
|
||||
if ( !empty( $_REQUEST['groups'] ) ) {
|
||||
if ( $_GET['groups-action'] == "add-group" ) {
|
||||
$action = 'add';
|
||||
} else if ( $_GET['groups-action'] == "remove-group" ) {
|
||||
$action = 'remove';
|
||||
}
|
||||
}
|
||||
if ( $users !== null && $action !== null ) {
|
||||
if ( wp_verify_nonce( $_REQUEST['bulk-user-group-nonce'], 'user-group' ) ) {
|
||||
foreach( $users as $user_id ) {
|
||||
switch ( $action ) {
|
||||
case 'add':
|
||||
$group_ids = isset( $_GET['group_ids'] ) ? $_GET['group_ids'] : null;
|
||||
if ( $group_ids !== null ) {
|
||||
foreach ( $group_ids as $group_id ) {
|
||||
if ( !Groups_User_Group::read( $user_id, $group_id ) ) {
|
||||
Groups_User_Group::create(
|
||||
array(
|
||||
'user_id' => $user_id,
|
||||
'group_id' => $group_id
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'remove':
|
||||
$group_ids = isset( $_GET['group_ids'] ) ? $_GET['group_ids'] : null;
|
||||
if ( $group_ids !== null ) {
|
||||
foreach ( $group_ids as $group_id ) {
|
||||
if ( Groups_User_Group::read( $user_id, $group_id ) ) {
|
||||
Groups_User_Group::delete( $user_id, $group_id );
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
$referer = wp_get_referer();
|
||||
if ( $referer ) {
|
||||
$redirect_to = remove_query_arg( array( 'action', 'action2', 'add-to-group', 'bulk-user-group-nonce', 'group_id', 'new_role', 'remove-from-group', 'users' ), $referer );
|
||||
wp_redirect( $redirect_to );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new column to the users table to show the groups that users
|
||||
* belong to.
|
||||
*
|
||||
* @param array $column_headers
|
||||
* @return array column headers
|
||||
*/
|
||||
public static function manage_users_columns( $column_headers ) {
|
||||
$column_headers[self::GROUPS] = __( 'Groups', 'groups' );
|
||||
return $column_headers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders custom column content.
|
||||
*
|
||||
* @param string $output
|
||||
* @param string $column_name
|
||||
* @param int $user_id
|
||||
* @return string custom column content
|
||||
*/
|
||||
public static function manage_users_custom_column( $output, $column_name, $user_id ) {
|
||||
switch ( $column_name ) {
|
||||
case self::GROUPS :
|
||||
$groups_user = new Groups_User( $user_id );
|
||||
$groups = $groups_user->groups;
|
||||
if ( count( $groups ) > 0 ) {
|
||||
usort( $groups, array( __CLASS__, 'by_group_name' ) );
|
||||
$output = '<ul>';
|
||||
foreach( $groups as $group ) {
|
||||
$output .= '<li>';
|
||||
$output .= wp_filter_nohtml_kses( $group->name );
|
||||
$output .= '</li>';
|
||||
}
|
||||
$output .= '</ul>';
|
||||
} else {
|
||||
$output .= __( '--', 'groups' );
|
||||
}
|
||||
break;
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* usort helper
|
||||
* @param Groups_Group $o1
|
||||
* @param Groups_Group $o2
|
||||
* @return int strcmp result for group names
|
||||
*/
|
||||
public static function by_group_name( $o1, $o2 ) {
|
||||
return strcmp( $o1->name, $o2->name );
|
||||
}
|
||||
}
|
||||
Groups_Admin_Users::init();
|
||||
@@ -0,0 +1,247 @@
|
||||
<?php
|
||||
/**
|
||||
* class-groups-admin-welcome.php
|
||||
*
|
||||
* Copyright (c) 2017 "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 2.0.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Groups admin welcome and update screen.
|
||||
*/
|
||||
class Groups_Admin_Welcome {
|
||||
|
||||
/**
|
||||
* Adds actions to admin_menu, admin_head and admin_init.
|
||||
*/
|
||||
public static function init() {
|
||||
add_action( 'admin_menu', array( __CLASS__, 'admin_menu' ) );
|
||||
add_action( 'admin_head', array( __CLASS__, 'admin_head' ) );
|
||||
add_action( 'admin_init', array( __CLASS__, 'admin_init' ) );
|
||||
add_filter( 'plugin_row_meta', array( __CLASS__, 'plugin_row_meta' ), 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the welcome screen to the dashboard menu.
|
||||
*/
|
||||
public static function admin_menu() {
|
||||
add_dashboard_page(
|
||||
__( 'Welcome to Groups', 'groups' ),
|
||||
__( 'Welcome to Groups', 'groups' ),
|
||||
'manage_options',
|
||||
'groups-welcome',
|
||||
array( __CLASS__, 'groups_welcome' )
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the welcome screen from the dashboard menu.
|
||||
*/
|
||||
public static function admin_head() {
|
||||
remove_submenu_page( 'index.php', 'groups-welcome' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the welcome screen should be shown and redirected to.
|
||||
*/
|
||||
public static function admin_init() {
|
||||
global $groups_version;
|
||||
if (
|
||||
current_user_can( GROUPS_ACCESS_GROUPS ) &&
|
||||
isset( $_GET['groups-welcome-dismiss'] ) &&
|
||||
isset( $_GET['_groups_welcome_nonce'] )
|
||||
) {
|
||||
if ( wp_verify_nonce( $_GET['_groups_welcome_nonce'], 'groups_welcome_dismiss' ) ) {
|
||||
Groups_Options::update_user_option( 'groups-welcome-dismiss', $groups_version );
|
||||
}
|
||||
}
|
||||
$groups_welcome_dismiss = Groups_Options::get_user_option( 'groups-welcome-dismiss', null );
|
||||
if ( version_compare( $groups_version, $groups_welcome_dismiss ) > 0 ) {
|
||||
if ( get_transient( 'groups_plugin_activated' ) || get_transient( 'groups_plugin_updated_legacy' ) ) {
|
||||
$doing_ajax = defined( 'DOING_AJAX' ) && DOING_AJAX;
|
||||
$doing_cron = defined( 'DOING_CRON' ) && DOING_CRON;
|
||||
// we'll delete the transients in the welcome screen handler
|
||||
if (
|
||||
!$doing_ajax &&
|
||||
!$doing_cron &&
|
||||
( empty( $_GET['page'] ) || $_GET['page'] !== 'groups-welcome' ) &&
|
||||
!is_network_admin() &&
|
||||
!isset( $_GET['activate-multi'] ) &&
|
||||
current_user_can( GROUPS_ACCESS_GROUPS ) &&
|
||||
apply_filters( 'groups_welcome_show', true )
|
||||
) {
|
||||
wp_safe_redirect( admin_url( 'index.php?page=groups-welcome' ) );
|
||||
exit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an entry leading to the welcome screen.
|
||||
*
|
||||
* @param array $links
|
||||
* @param string $file plugin file basename
|
||||
* @return array
|
||||
*/
|
||||
public static function plugin_row_meta( $links, $file ) {
|
||||
if ( $file == plugin_basename( GROUPS_FILE ) ) {
|
||||
$row_meta = array(
|
||||
'welcome' => sprintf(
|
||||
'<a href="%s" title="%s">%s</a>',
|
||||
esc_url( admin_url( 'index.php?page=groups-welcome' ) ),
|
||||
esc_attr( __( 'View the Welcome screen for this version of Groups', 'groups' ) ),
|
||||
esc_html( __( 'Welcome', 'groups' ) )
|
||||
)
|
||||
);
|
||||
return array_merge( $links, $row_meta );
|
||||
}
|
||||
return (array) $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the welcome screen.
|
||||
*/
|
||||
public static function groups_welcome() {
|
||||
|
||||
global $groups_version;
|
||||
|
||||
wp_enqueue_style( 'groups_admin' );
|
||||
|
||||
delete_transient( 'groups_plugin_activated' );
|
||||
$legacy_update = get_transient( 'groups_plugin_updated_legacy' );
|
||||
delete_transient( 'groups_plugin_updated_legacy' );
|
||||
|
||||
echo '<div class="groups-welcome-panel">';
|
||||
echo '<div class="groups-welcome-panel-content">';
|
||||
|
||||
printf( '<img class="groups-welcome-icon" width="64" height="64" src="%s"/>', esc_attr( GROUPS_PLUGIN_URL . 'images/groups-256x256.png' ) );
|
||||
|
||||
echo '<h1>';
|
||||
printf( __( 'Welcome to Groups %s', 'groups' ), esc_html( $groups_version ) );
|
||||
echo '</h1>';
|
||||
|
||||
printf(
|
||||
'<a class="notice-dismiss" href="%s" title="%s" aria-label="%s"></a>',
|
||||
esc_url( wp_nonce_url( add_query_arg( 'groups-welcome-dismiss', '1', admin_url() ), 'groups_welcome_dismiss', '_groups_welcome_nonce' ) ),
|
||||
esc_attr( __( 'Dismiss', 'groups' ) ),
|
||||
esc_html( __( 'Dismiss', 'groups' ) )
|
||||
);
|
||||
|
||||
echo '<p class="headline">';
|
||||
_e( 'Thanks for using Groups! We have made it even easier to protect your content and hope you like it :)', 'groups' );
|
||||
echo '</p>';
|
||||
|
||||
if ( $legacy_update ) {
|
||||
echo '<p class="important">';
|
||||
echo '<strong>';
|
||||
_e( 'Important', 'groups' );
|
||||
echo '</strong>';
|
||||
echo '<br/><br/>';
|
||||
_e( 'It seems that you have updated from Groups 1.x where access restrictions were based on capabilities.', 'groups' );
|
||||
echo '<br/>';
|
||||
printf( __( 'Please make sure to read the notes on <strong>Switching to Groups %s</strong> below.', 'groups' ), esc_html( $groups_version ) );
|
||||
echo '</p>';
|
||||
}
|
||||
|
||||
echo '<h2>';
|
||||
_e( "What's New?", 'groups' );
|
||||
echo '</h2>';
|
||||
|
||||
echo '<h3>';
|
||||
_e( 'Protect Content Easily', 'groups' );
|
||||
echo '</h3>';
|
||||
echo '<p>';
|
||||
_e( 'We have made it even easier to protect your content!', 'groups' );
|
||||
echo ' ';
|
||||
_e( 'Now you can protect your posts, pages and any other custom post type like products or events by simply assigning them to one or more groups.', 'groups' );
|
||||
echo ' ';
|
||||
_e( 'Previously we used capabilities to do that, but changing to this new model makes things even easier.', 'groups' );
|
||||
echo '</p>';
|
||||
|
||||
echo '<h3>';
|
||||
_e( 'Improved User Interface', 'groups' );
|
||||
echo '</h3>';
|
||||
echo '<p>';
|
||||
_e( 'Now you can assign new users directly to groups when you create a new user account from the Dashboard.', 'groups' );
|
||||
echo ' ';
|
||||
_e( 'Another improvement is better filtering by groups and a reduced footprint on the Users admin screen.', 'groups' );
|
||||
echo ' ';
|
||||
_e( 'And you can now filter the list of users by one or multiple groups with one convenient field.', 'groups' );
|
||||
echo '</p>';
|
||||
|
||||
echo '<h3>';
|
||||
_e( 'New Documentation', 'groups' );
|
||||
echo '</h3>';
|
||||
echo '<p>';
|
||||
_e( 'Whether you are new to Groups or have been using it before, please make sure to visit the <a target="_blank" href="http://docs.itthinx.com/document/groups/">Documentation</a> pages to know more about how to use it.', 'groups' );
|
||||
echo '</p>';
|
||||
|
||||
$legacy_enabled = Groups_Options::get_option( GROUPS_LEGACY_ENABLE );
|
||||
echo '<h2>';
|
||||
printf( __( 'Switching to Groups %s', 'groups' ), esc_html( $groups_version ) );
|
||||
echo '</h2>';
|
||||
echo '<p>';
|
||||
printf( __( 'Groups %s features a simpler model for access restrictions based on groups instead of capabilities used in previous versions.', 'groups' ), esc_html( $groups_version ) );
|
||||
echo ' ';
|
||||
_e( 'To put it simple, previously you would have used capabilities to restrict access to posts and now you simply use groups.', 'groups' );
|
||||
echo ' ';
|
||||
_e( 'To make it easier to transition to the new model for those who migrate from a previous version, we have included legacy access control based on capabilities.', 'groups' );
|
||||
echo '</p>';
|
||||
echo '<div class="indent">';
|
||||
echo '<p>';
|
||||
_e( 'The following is only of interest if you have upgraded from Groups 1.x:', 'groups' );
|
||||
echo '<br/>';
|
||||
if ( $legacy_enabled ) {
|
||||
_e( 'You are running the system with legacy access control based on capabilities enabled.', 'groups' );
|
||||
echo ' ';
|
||||
_e( 'This means that if you had access restrictions in place that were based on capabilities, your entries will still be protected.', 'groups' );
|
||||
} else {
|
||||
_e( 'You are running the system with legacy access control based on capabilities disabled.', 'groups' );
|
||||
echo ' ';
|
||||
_e( 'This could be important!', 'groups' );
|
||||
echo ' ';
|
||||
_e( 'If you had any access restrictions in place based on capabilities, the entries will now be unprotected, unless you enable legacy access restrictions or place appropriate access restrictions based on groups on the desired entries.', 'groups' );
|
||||
}
|
||||
echo '</p>';
|
||||
echo '<p>';
|
||||
_e( 'If you would like to switch to access restrictions based on groups (recommended) instead of capabilities, you can easily do so by setting the appropriate groups on your protected posts, pages and other entries to restrict access.', 'groups' );
|
||||
echo ' ';
|
||||
_e( 'Once you have adjusted your access restrictions based on groups, you can disable legacy access control.', 'groups' );
|
||||
echo ' ';
|
||||
_e( 'Please refer to the <a target="_blank" href="http://docs.itthinx.com/document/groups/">Documentation</a> for details on how to switch to and use the new access restrictions.', 'groups' );
|
||||
echo '</p>';
|
||||
echo '</div>'; // .indent
|
||||
|
||||
echo '<h2>';
|
||||
_e( 'Add-Ons', 'groups' );
|
||||
echo '</h2>';
|
||||
echo '<p>';
|
||||
_e( 'Perfect complements to memberships and access control with Groups.', 'groups' );
|
||||
echo '</p>';
|
||||
echo '<div class="groups-admin-add-ons">';
|
||||
groups_admin_add_ons_content( array( 'offset' => 1 ) );
|
||||
echo '</div>'; // .groups-admin-add-ons
|
||||
|
||||
echo '</div>'; // .groups-welcome-panel-content
|
||||
echo '</div>'; // .groups-welcome-panel
|
||||
}
|
||||
}
|
||||
Groups_Admin_Welcome::init();
|
||||
@@ -0,0 +1,315 @@
|
||||
<?php
|
||||
/**
|
||||
* class-groups-admin.php
|
||||
*
|
||||
* Copyright (c) 2011 "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.0.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Groups admin sections initialization.
|
||||
*/
|
||||
class Groups_Admin {
|
||||
|
||||
/**
|
||||
* The position of the Groups menu.
|
||||
*
|
||||
* @var int
|
||||
*/
|
||||
const MENU_POSITION = '38.381';
|
||||
|
||||
/**
|
||||
* Holds admin messages.
|
||||
* @var string
|
||||
*/
|
||||
private static $messages = array();
|
||||
|
||||
/**
|
||||
* Sets up action hooks.
|
||||
*/
|
||||
public static function init() {
|
||||
add_action( 'admin_init', array( __CLASS__, 'admin_init' ) );
|
||||
add_action( 'admin_notices', array( __CLASS__, 'admin_notices' ) );
|
||||
add_action( 'admin_head', array( __CLASS__, 'admin_head' ) );
|
||||
add_action( 'admin_menu', array( __CLASS__, 'admin_menu' ) );
|
||||
add_action( 'network_admin_menu', array( __CLASS__, 'network_admin_menu' ) );
|
||||
add_filter( 'plugin_action_links_'. plugin_basename( GROUPS_FILE ), array( __CLASS__, 'plugin_action_links' ) );
|
||||
add_action( 'after_plugin_row_' . plugin_basename( GROUPS_FILE ), array( __CLASS__, 'after_plugin_row' ), 10, 3 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooks into admin_init.
|
||||
* @see Groups_Admin::admin_menu()
|
||||
* @see Groups_Admin::admin_print_styles()
|
||||
* @link http://codex.wordpress.org/Function_Reference/wp_enqueue_script#Load_scripts_only_on_plugin_pages
|
||||
*/
|
||||
public static function admin_init() {
|
||||
global $groups_version;
|
||||
wp_register_style( 'groups_admin', GROUPS_PLUGIN_URL . 'css/groups_admin.css', array(), $groups_version );
|
||||
require_once GROUPS_VIEWS_LIB . '/class-groups-uie.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads styles for the Groups admin section.
|
||||
*
|
||||
* @see Groups_Admin::admin_menu()
|
||||
*/
|
||||
public static function admin_print_styles() {
|
||||
wp_enqueue_style( 'groups_admin' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads scripts.
|
||||
*/
|
||||
public static function admin_print_scripts() {
|
||||
global $groups_version;
|
||||
// this one's currently empty
|
||||
//wp_enqueue_script( 'groups_admin', GROUPS_PLUGIN_URL . 'js/groups_admin.js', array( ), $groups_version );
|
||||
Groups_UIE::enqueue( 'select' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a message to the list of messages displayed in the admin sections.
|
||||
* The message is filtered using wp_filter_kses() and wrapped in a div
|
||||
* with class 'updated' for messages of type 'info' and 'error' for
|
||||
* those of type 'error'.
|
||||
*
|
||||
* @param string $message the message
|
||||
* @param string $type type of message, defaults to 'info'
|
||||
* @uses wp_filter_kses()
|
||||
*/
|
||||
public static function add_message( $message, $type = 'info' ) {
|
||||
$class = 'updated';
|
||||
switch( $type ) {
|
||||
case 'error' :
|
||||
$class = 'error';
|
||||
}
|
||||
self::$messages[] = '<div class="'.$class.'">' . balanceTags( stripslashes( wp_filter_kses( $message ) ), true ) . '</div>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of messages as a string.
|
||||
* An empty string is returned if there are no messages.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function render_messages() {
|
||||
$output = '';
|
||||
if ( !empty( self::$messages ) ) {
|
||||
$output .= '<div class="groups messages">';
|
||||
$output .= implode( '', self::$messages );
|
||||
$output .= '</div>';
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints admin notices.
|
||||
*/
|
||||
public static function admin_notices() {
|
||||
global $groups_admin_messages;
|
||||
if ( !empty( $groups_admin_messages ) ) {
|
||||
foreach ( $groups_admin_messages as $msg ) {
|
||||
echo $msg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Use a context-sensitive menu item title.
|
||||
*/
|
||||
public static function admin_head() {
|
||||
global $submenu;
|
||||
if ( isset( $submenu['groups-admin'] ) ) {
|
||||
$submenu['groups-admin'][0][0] = _x( 'Groups', 'menu item title', 'groups' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Admin menu.
|
||||
*/
|
||||
public static function admin_menu() {
|
||||
|
||||
include_once( GROUPS_ADMIN_LIB . '/groups-admin-groups.php');
|
||||
include_once( GROUPS_ADMIN_LIB . '/groups-admin-capabilities.php');
|
||||
include_once( GROUPS_ADMIN_LIB . '/groups-admin-options.php');
|
||||
include_once( GROUPS_ADMIN_LIB . '/groups-admin-add-ons.php');
|
||||
|
||||
$pages = array();
|
||||
|
||||
// main
|
||||
$page = add_menu_page(
|
||||
_x( 'Groups', 'page-title', 'groups' ),
|
||||
'Groups', // don't translate, reasons: a) Groups menu title consistency and b) http://core.trac.wordpress.org/ticket/18857 translation affects $screen->id
|
||||
GROUPS_ADMINISTER_GROUPS,
|
||||
'groups-admin',
|
||||
apply_filters( 'groups_add_menu_page_function', 'groups_admin_groups' ),
|
||||
GROUPS_PLUGIN_URL . '/images/groups.png',
|
||||
self::MENU_POSITION
|
||||
);
|
||||
$pages[] = $page;
|
||||
add_action( 'admin_print_styles-' . $page, array( __CLASS__, 'admin_print_styles' ) );
|
||||
add_action( 'admin_print_scripts-' . $page, array( __CLASS__, 'admin_print_scripts' ) );
|
||||
|
||||
if ( isset( $_POST[GROUPS_ADMIN_OPTIONS_NONCE] ) && wp_verify_nonce( $_POST[GROUPS_ADMIN_OPTIONS_NONCE], 'admin' ) ) {
|
||||
$show_tree_view = !empty( $_POST[GROUPS_SHOW_TREE_VIEW] );
|
||||
} else {
|
||||
$show_tree_view = Groups_Options::get_option( GROUPS_SHOW_TREE_VIEW, GROUPS_SHOW_TREE_VIEW_DEFAULT );
|
||||
}
|
||||
|
||||
if ( $show_tree_view ) {
|
||||
include_once( GROUPS_ADMIN_LIB . '/groups-admin-tree-view.php');
|
||||
$page = add_submenu_page(
|
||||
'groups-admin',
|
||||
__( 'Tree', 'groups' ),
|
||||
__( 'Tree', 'groups' ),
|
||||
GROUPS_ACCESS_GROUPS,
|
||||
'groups-admin-tree-view',
|
||||
apply_filters( 'groups_add_submenu_page_function', 'groups_admin_tree_view' )
|
||||
);
|
||||
$pages[] = $page;
|
||||
add_action( 'admin_print_styles-' . $page, array( __CLASS__, 'admin_print_styles' ) );
|
||||
add_action( 'admin_print_scripts-' . $page, array( __CLASS__, 'admin_print_scripts' ) );
|
||||
}
|
||||
|
||||
// capabilities
|
||||
$page = add_submenu_page(
|
||||
'groups-admin',
|
||||
__( 'Groups Capabilities', 'groups' ),
|
||||
__( 'Capabilities', 'groups' ),
|
||||
GROUPS_ADMINISTER_GROUPS,
|
||||
'groups-admin-capabilities',
|
||||
apply_filters( 'groups_add_submenu_page_function', 'groups_admin_capabilities' )
|
||||
);
|
||||
$pages[] = $page;
|
||||
add_action( 'admin_print_styles-' . $page, array( __CLASS__, 'admin_print_styles' ) );
|
||||
add_action( 'admin_print_scripts-' . $page, array( __CLASS__, 'admin_print_scripts' ) );
|
||||
|
||||
// options
|
||||
$page = add_submenu_page(
|
||||
'groups-admin',
|
||||
__( 'Groups options', 'groups' ),
|
||||
__( 'Options', 'groups' ),
|
||||
GROUPS_ADMINISTER_OPTIONS,
|
||||
'groups-admin-options',
|
||||
apply_filters( 'groups_add_submenu_page_function', 'groups_admin_options' )
|
||||
);
|
||||
$pages[] = $page;
|
||||
add_action( 'admin_print_styles-' . $page, array( __CLASS__, 'admin_print_styles' ) );
|
||||
add_action( 'admin_print_scripts-' . $page, array( __CLASS__, 'admin_print_scripts' ) );
|
||||
|
||||
// add-ons
|
||||
$page = add_submenu_page(
|
||||
'groups-admin',
|
||||
__( 'Groups Add-Ons', 'groups' ),
|
||||
__( 'Add-Ons', 'groups' ),
|
||||
GROUPS_ACCESS_GROUPS,
|
||||
'groups-admin-add-ons',
|
||||
apply_filters( 'groups_add_submenu_page_function', 'groups_admin_add_ons' )
|
||||
);
|
||||
$pages[] = $page;
|
||||
add_action( 'admin_print_styles-' . $page, array( __CLASS__, 'admin_print_styles' ) );
|
||||
add_action( 'admin_print_scripts-' . $page, array( __CLASS__, 'admin_print_scripts' ) );
|
||||
|
||||
do_action( 'groups_admin_menu', $pages );
|
||||
}
|
||||
|
||||
/**
|
||||
* Network admin menu.
|
||||
*/
|
||||
public static function network_admin_menu() {
|
||||
|
||||
include_once( GROUPS_ADMIN_LIB . '/groups-admin-options.php');
|
||||
|
||||
$pages = array();
|
||||
|
||||
// main
|
||||
$page = add_menu_page(
|
||||
__( 'Groups', 'groups' ),
|
||||
__( 'Groups', 'groups' ),
|
||||
GROUPS_ADMINISTER_GROUPS,
|
||||
'groups-network-admin',
|
||||
apply_filters( 'groups_add_menu_page_function', 'groups_network_admin_options' ),
|
||||
GROUPS_PLUGIN_URL . '/images/groups.png'
|
||||
);
|
||||
$pages[] = $page;
|
||||
add_action( 'admin_print_styles-' . $page, array( __CLASS__, 'admin_print_styles' ) );
|
||||
add_action( 'admin_print_scripts-' . $page, array( __CLASS__, 'admin_print_scripts' ) );
|
||||
|
||||
do_action( 'groups_network_admin_menu', $pages );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds plugin links.
|
||||
*
|
||||
* @param array $links
|
||||
* @param array $links with additional links
|
||||
*/
|
||||
public static function plugin_action_links( $links ) {
|
||||
if ( current_user_can( GROUPS_ADMINISTER_OPTIONS ) ) {
|
||||
array_unshift(
|
||||
$links,
|
||||
'<a href="' . get_admin_url( null, 'admin.php?page=groups-admin-options' ) . '">' . __( 'Options', 'groups' ) . '</a>'
|
||||
);
|
||||
}
|
||||
if ( current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
|
||||
array_unshift(
|
||||
$links,
|
||||
'<a href="' . get_admin_url( null, 'admin.php?page=groups-admin' ) . '">' . __( 'Groups', 'groups' ) . '</a>'
|
||||
);
|
||||
}
|
||||
return $links;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a warning when data is deleted on deactivation.
|
||||
*
|
||||
* @param string $plugin_file
|
||||
* @param array $plugin_data
|
||||
* @param string $status
|
||||
*/
|
||||
public static function after_plugin_row( $plugin_file, $plugin_data, $status ) {
|
||||
if ( $plugin_file == plugin_basename( GROUPS_FILE ) ) {
|
||||
$delete_data = Groups_Options::get_option( 'groups_delete_data', false );
|
||||
$delete_network_data = Groups_Options::get_option( 'groups_network_delete_data', false );
|
||||
if (
|
||||
( is_plugin_active( $plugin_file ) && $delete_data && current_user_can( 'install_plugins' ) ) ||
|
||||
( is_plugin_active_for_network( $plugin_file ) && $delete_network_data && current_user_can( 'manage_network_plugins' ) )
|
||||
) {
|
||||
echo '<tr class="active">';
|
||||
echo '<td> </td>';
|
||||
echo '<td colspan="2">';
|
||||
echo '<div style="border: 2px solid #dc3232; padding: 1em">';
|
||||
echo '<p>';
|
||||
echo '<strong>';
|
||||
echo esc_html( __( 'Warning!', 'groups' ) );
|
||||
echo '</strong>';
|
||||
echo '</p>';
|
||||
echo '<p>';
|
||||
echo esc_html( __( 'Groups is configured to delete its plugin data on deactivation.', 'groups' ) );
|
||||
echo '</p>';
|
||||
echo '</div>';
|
||||
echo '</td>';
|
||||
echo '</tr>';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Groups_Admin::init();
|
||||
@@ -0,0 +1,202 @@
|
||||
<?php
|
||||
/**
|
||||
* groups-admin-add-ons.php
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.8.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the heading and content container for the Add-Ons section.
|
||||
*/
|
||||
function groups_admin_add_ons() {
|
||||
echo '<div class="groups-admin-add-ons wrap">';
|
||||
echo '<h1>';
|
||||
echo __( 'Add-Ons', 'groups' );
|
||||
echo '</h1>';
|
||||
groups_admin_add_ons_content();
|
||||
echo '</div>'; // .groups-admin-add-ons.wrap
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the content of the Add-Ons section.
|
||||
*
|
||||
* @param $params array of options (offset is 0 by default and used to adjust heading h2)
|
||||
*/
|
||||
function groups_admin_add_ons_content( $params = array( 'offset' => 0 ) ) {
|
||||
|
||||
$d = intval( $params['offset'] );
|
||||
$h2 = sprintf( 'h%d', 2+$d );
|
||||
|
||||
echo "<$h2>";
|
||||
echo __( 'Recommended extensions for Groups', 'groups' );
|
||||
echo "</$h2>";
|
||||
|
||||
$entries = array(
|
||||
'groups-file-access' => array(
|
||||
'title' => 'Groups File Access',
|
||||
'content' => 'Groups File Access is a WordPress plugin that allows to provide file download links for authorized users. Access to files is restricted to users by their group membership.',
|
||||
'image' => GROUPS_PLUGIN_URL . 'images/add-ons/groups-file-access.png',
|
||||
'url' => 'http://www.itthinx.com/shop/groups-file-access/',
|
||||
'index' => 100
|
||||
),
|
||||
'groups-forums' => array(
|
||||
'title' => 'Groups Forums',
|
||||
'content' => 'Groups Forums provides a powerful and yet light-weight forum system for WordPress sites.',
|
||||
'image' => GROUPS_PLUGIN_URL . 'images/add-ons/groups-forums.png',
|
||||
'url' => 'http://www.itthinx.com/shop/groups-forums/',
|
||||
'index' => 100
|
||||
),
|
||||
'groups-gravity-forms' => array(
|
||||
'title' => 'Groups Gravity Forms',
|
||||
'content' => 'This extension integrates Groups with Gravity Forms. It allows to add users to groups automatically, based on form submissions.',
|
||||
'image' => GROUPS_PLUGIN_URL . 'images/add-ons/groups-gravity-forms.png',
|
||||
'url' => 'http://www.itthinx.com/shop/groups-gravity-forms/',
|
||||
'index' => 100
|
||||
),
|
||||
'groups-import-export' => array(
|
||||
'title' => 'Groups Import Export',
|
||||
'content' => 'This is an extension for Groups, providing import and export facilities. Users can be imported and assigned to groups in bulk from a text file. Users can be exported in bulk, including all users or users that belong to specific groups.',
|
||||
'image' => GROUPS_PLUGIN_URL . 'images/add-ons/groups-import-export.png',
|
||||
'url' => 'http://www.itthinx.com/shop/groups-import-export/',
|
||||
'index' => 100
|
||||
),
|
||||
'groups-newsletters' => array(
|
||||
'title' => 'Groups Newsletters',
|
||||
'content' => 'Newsletter Campaigns for Subscribers and Groups. Groups Newsletters helps you to communicate efficiently, providing targeted information to groups of recipients through automated campaigns.',
|
||||
'image' => GROUPS_PLUGIN_URL . 'images/add-ons/groups-newsletters.png',
|
||||
'url' => 'http://www.itthinx.com/shop/groups-newsletters/',
|
||||
'index' => 100
|
||||
),
|
||||
'groups-paypal' => array(
|
||||
'title' => 'Groups PayPal',
|
||||
'content' => 'Sell memberships and subscriptions with Groups and PayPal.',
|
||||
'image' => GROUPS_PLUGIN_URL . 'images/add-ons/groups-paypal.png',
|
||||
'url' => 'http://www.itthinx.com/shop/groups-paypal/',
|
||||
'index' => 10
|
||||
),
|
||||
'groups-restrict-categories' => array(
|
||||
'title' => 'Groups Restrict Categories',
|
||||
'content' => 'Access restrictions for categories and tags, also supporting custom post types and taxonomies.',
|
||||
'image' => GROUPS_PLUGIN_URL . 'images/add-ons/groups-restrict-categories.png',
|
||||
'url' => 'http://www.itthinx.com/shop/groups-restrict-categories/',
|
||||
'index' => 10
|
||||
),
|
||||
'groups-restrict-comments-pro' => array(
|
||||
'title' => 'Groups Restrict Comments Pro',
|
||||
'content' => 'This extension allows to restrict who can post or read comments based on a user’s group membership.',
|
||||
'image' => GROUPS_PLUGIN_URL . 'images/add-ons/groups-restrict-comments-pro.png',
|
||||
'url' => 'http://www.itthinx.com/shop/groups-restrict-comments-pro/',
|
||||
'index' => 100
|
||||
),
|
||||
'groups-woocommerce' => array(
|
||||
'title' => 'Groups WooCommerce',
|
||||
'content' => 'This extension allows you to sell memberships with WooCommerce.',
|
||||
'image' => GROUPS_PLUGIN_URL . 'images/add-ons/groups-woocommerce.png',
|
||||
'url' => 'http://www.itthinx.com/shop/groups-woocommerce/',
|
||||
'index' => 20
|
||||
),
|
||||
'widgets-control-pro' => array(
|
||||
'title' => 'Widgets Control Pro',
|
||||
'content' => 'An advanced Widget toolbox that adds visibility management and helps to control where widgets are shown efficiently. Show or hide widgets based on a user’s group membership.',
|
||||
'image' => GROUPS_PLUGIN_URL . 'images/add-ons/widgets-control-pro.png',
|
||||
'url' => 'http://www.itthinx.com/shop/widgets-control-pro/',
|
||||
'index' => 20
|
||||
),
|
||||
'woocommerce-group-coupons' => array(
|
||||
'title' => 'WooCommerce Group Coupons',
|
||||
'content' => 'This extension allows to limit the validity of coupons based on groups and roles.',
|
||||
'image' => GROUPS_PLUGIN_URL . 'images/add-ons/woocommerce-group-coupons.png',
|
||||
'url' => 'http://www.itthinx.com/shop/woocommerce-group-coupons/',
|
||||
'index' => 100
|
||||
),
|
||||
'woocommerce-groups-newsletters' => array(
|
||||
'title' => 'WooCommerce Groups Newsletters',
|
||||
'content' => 'The WooCommerce Groups Newsletters extension lets customers subscribe to newsletters at checkout.',
|
||||
'image' => GROUPS_PLUGIN_URL . 'images/add-ons/woocommerce-groups-newsletters.png',
|
||||
'url' => 'http://www.itthinx.com/shop/woocommerce-groups-newsletters/',
|
||||
'index' => 100
|
||||
),
|
||||
);
|
||||
usort( $entries, 'groups_admin_add_ons_sort' );
|
||||
|
||||
echo '<ul class="add-ons">';
|
||||
foreach( $entries as $key => $entry ) {
|
||||
echo '<li class="add-on">';
|
||||
echo sprintf( '<a href="%s">', $entry['url'] );
|
||||
echo '<h3>';
|
||||
echo sprintf( '<img src="%s"/>', $entry['image'] );
|
||||
echo $entry['title'];
|
||||
echo '</h3>';
|
||||
echo '<p>';
|
||||
echo $entry['content'];
|
||||
echo '</p>';
|
||||
echo '</a>';
|
||||
echo '</li>'; // .add-on
|
||||
}
|
||||
echo '</ul>'; // .add-ons
|
||||
|
||||
echo "<$h2>";
|
||||
echo __( 'Recommended plugins by itthinx', 'groups' );
|
||||
echo "</$h2>";
|
||||
|
||||
$entries = array(
|
||||
'affiliates-pro' => array(
|
||||
'title' => 'Affiliates Pro',
|
||||
'content' => 'Boost Sales with Affiliate Marketing for your WordPress site.',
|
||||
'image' => GROUPS_PLUGIN_URL . 'images/add-ons/affiliates-pro.png',
|
||||
'url' => 'http://www.itthinx.com/shop/affiliates-pro/',
|
||||
'index' => 100
|
||||
),
|
||||
'affiliates-enterprise' => array(
|
||||
'title' => 'Affiliates Enterprise',
|
||||
'content' => 'Affiliates Enterprise provides an affiliate management system for sellers, shops and developers, who want to boost sales with their own affiliate program. Features affiliate campaigns, tracking pixels and multiple tiers.',
|
||||
'image' => GROUPS_PLUGIN_URL . 'images/add-ons/affiliates-enterprise.png',
|
||||
'url' => 'http://www.itthinx.com/shop/affiliates-enterprise/',
|
||||
'index' => 100
|
||||
),
|
||||
);
|
||||
usort( $entries, 'groups_admin_add_ons_sort' );
|
||||
|
||||
echo '<ul class="add-ons">';
|
||||
foreach( $entries as $key => $entry ) {
|
||||
echo '<li class="add-on">';
|
||||
echo sprintf( '<a href="%s">', $entry['url'] );
|
||||
echo '<h3>';
|
||||
echo sprintf( '<img src="%s"/>', $entry['image'] );
|
||||
echo $entry['title'];
|
||||
echo '</h3>';
|
||||
echo '<p>';
|
||||
echo $entry['content'];
|
||||
echo '</p>';
|
||||
echo '</a>';
|
||||
echo '</li>'; // .add-on
|
||||
}
|
||||
echo '</ul>'; // .add-ons
|
||||
}
|
||||
|
||||
function groups_admin_add_ons_sort( $e1, $e2 ) {
|
||||
$i1 = isset( $e1['index'] ) ? $e1['index'] : 0;
|
||||
$i2 = isset( $e2['index'] ) ? $e2['index'] : 0;
|
||||
$t1 = isset( $e1['title'] ) ? $e1['title'] : '';
|
||||
$t2 = isset( $e2['title'] ) ? $e2['title'] : '';
|
||||
|
||||
return $i1 - $i2 + strnatcmp( $t1, $t2 );
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
/**
|
||||
* groups-admin-capabilities-add.php
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.0.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show add capability form.
|
||||
*/
|
||||
function groups_admin_capabilities_add() {
|
||||
|
||||
global $wpdb;
|
||||
|
||||
if ( !current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
|
||||
wp_die( __( 'Access denied.', 'groups' ) );
|
||||
}
|
||||
|
||||
$current_url = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
|
||||
$current_url = remove_query_arg( 'paged', $current_url );
|
||||
$current_url = remove_query_arg( 'action', $current_url );
|
||||
$current_url = remove_query_arg( 'capability_id', $current_url );
|
||||
|
||||
$capability = isset( $_POST['capability-field'] ) ? $_POST['capability-field'] : '';
|
||||
$description = isset( $_POST['description-field'] ) ? $_POST['description-field'] : '';
|
||||
|
||||
$capability_table = _groups_get_tablename( 'capability' );
|
||||
|
||||
$output =
|
||||
'<div class="manage-capabilities wrap">' .
|
||||
'<h1>' .
|
||||
__( 'Add a new capability', 'groups' ) .
|
||||
'</h1>' .
|
||||
Groups_Admin::render_messages() .
|
||||
'<form id="add-capability" action="' . esc_url( $current_url ) . '" method="post">' .
|
||||
'<div class="capability new">' .
|
||||
|
||||
'<div class="field">' .
|
||||
'<label for="capability-field" class="field-label first required">' .__( 'Capability', 'groups' ) . '</label>' .
|
||||
'<input id="name-field" name="capability-field" class="capability-field" type="text" value="' . esc_attr( stripslashes( $capability ) ) . '"/>' .
|
||||
'</div>' .
|
||||
|
||||
'<div class="field">' .
|
||||
'<label for="description-field" class="field-label description-field">' .__( 'Description', 'groups' ) . '</label>' .
|
||||
'<textarea id="description-field" name="description-field" rows="5" cols="45">' . stripslashes( wp_filter_nohtml_kses( $description ) ) . '</textarea>' .
|
||||
'</div>' .
|
||||
|
||||
'<div class="field">' .
|
||||
wp_nonce_field( 'capabilities-add', GROUPS_ADMIN_GROUPS_NONCE, true, false ) .
|
||||
'<input class="button button-primary" type="submit" value="' . __( 'Add', 'groups' ) . '"/>' .
|
||||
'<input type="hidden" value="add" name="action"/>' .
|
||||
'<a class="cancel button" href="' . esc_url( $current_url ) . '">' . __( 'Cancel', 'groups' ) . '</a>' .
|
||||
'</div>' .
|
||||
'</div>' . // .capability.new
|
||||
'</form>' .
|
||||
'</div>'; // .manage-capabilities
|
||||
|
||||
echo $output;
|
||||
|
||||
} // function groups_admin_capabilities_add
|
||||
|
||||
/**
|
||||
* Handle add capability form submission.
|
||||
* @return int new capability's id or false if unsuccessful
|
||||
*/
|
||||
function groups_admin_capabilities_add_submit() {
|
||||
|
||||
global $wpdb;
|
||||
|
||||
if ( !current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
|
||||
wp_die( __( 'Access denied.', 'groups' ) );
|
||||
}
|
||||
|
||||
if ( !wp_verify_nonce( $_POST[GROUPS_ADMIN_GROUPS_NONCE], 'capabilities-add' ) ) {
|
||||
wp_die( __( 'Access denied.', 'groups' ) );
|
||||
}
|
||||
|
||||
$capability = isset( $_POST['capability-field'] ) ? $_POST['capability-field'] : null;
|
||||
$description = isset( $_POST['description-field'] ) ? $_POST['description-field'] : '';
|
||||
|
||||
$capability_id = Groups_Capability::create( compact( "capability", "description" ) );
|
||||
if ( !$capability_id ) {
|
||||
if ( empty( $capability ) ) {
|
||||
Groups_Admin::add_message( __( 'The <em>Capability</em> must not be empty.', 'groups' ), 'error' );
|
||||
} else if ( Groups_Capability::read_by_capability( $capability ) ) {
|
||||
Groups_Admin::add_message( sprintf( __( 'The <em>%s</em> capability already exists.', 'groups' ), stripslashes( wp_filter_nohtml_kses( ( $capability ) ) ) ), 'error' );
|
||||
}
|
||||
}
|
||||
return $capability_id;
|
||||
} // function groups_admin_capabilities_add_submit
|
||||
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
/**
|
||||
* groups-admin-capability-edit.php
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.0.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show edit capability form.
|
||||
* @param int $capability_id capability id
|
||||
*/
|
||||
function groups_admin_capabilities_edit( $capability_id ) {
|
||||
|
||||
global $wpdb;
|
||||
|
||||
if ( !current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
|
||||
wp_die( __( 'Access denied.', 'groups' ) );
|
||||
}
|
||||
|
||||
$capability = Groups_Capability::read( intval( $capability_id ) );
|
||||
|
||||
if ( empty( $capability ) ) {
|
||||
wp_die( __( 'No such capability.', 'groups' ) );
|
||||
}
|
||||
|
||||
$current_url = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
|
||||
$current_url = remove_query_arg( 'action', $current_url );
|
||||
$current_url = remove_query_arg( 'capability_id', $current_url );
|
||||
|
||||
$capability_capability = isset( $_POST['capability-field'] ) ? $_POST['capability-field'] : $capability->capability;
|
||||
$description = isset( $_POST['description-field'] ) ? $_POST['description-field'] : $capability->description;
|
||||
|
||||
$capability_readonly = ( $capability->capability !== Groups_Post_Access::READ_POST_CAPABILITY ) ? "" : ' readonly="readonly" ';
|
||||
|
||||
$output =
|
||||
'<div class="manage-capabilities wrap">' .
|
||||
'<h1>' .
|
||||
__( 'Edit a capability', 'groups' ) .
|
||||
'</h1>' .
|
||||
|
||||
Groups_Admin::render_messages() .
|
||||
|
||||
'<form id="edit-capability" action="' . esc_url( $current_url ) . '" method="post">' .
|
||||
'<div class="capability edit">' .
|
||||
'<input id="capability-id-field" name="capability-id-field" type="hidden" value="' . esc_attr( intval( $capability_id ) ) . '"/>' .
|
||||
|
||||
'<div class="field">' .
|
||||
'<label for="capability-field" class="field-label first required">' .__( 'Capability', 'groups' ) . '</label>' .
|
||||
'<input ' . $capability_readonly . ' id="capability-field" name="capability-field" class="capability-field" type="text" value="' . esc_attr( stripslashes( $capability_capability ) ) . '"/>' .
|
||||
'</div>' .
|
||||
|
||||
'<div class="field">' .
|
||||
'<label for="description-field" class="field-label description-field">' .__( 'Description', 'groups' ) . '</label>' .
|
||||
'<textarea id="description-field" name="description-field" rows="5" cols="45">' . stripslashes( wp_filter_nohtml_kses( $description ) ) . '</textarea>' .
|
||||
'</div>' .
|
||||
|
||||
'<div class="field">' .
|
||||
wp_nonce_field( 'capabilities-edit', GROUPS_ADMIN_GROUPS_NONCE, true, false ) .
|
||||
'<input class="button button-primary" type="submit" value="' . __( 'Save', 'groups' ) . '"/>' .
|
||||
'<input type="hidden" value="edit" name="action"/>' .
|
||||
'<a class="cancel button" href="' . esc_url( $current_url ) . '">' . __( 'Cancel', 'groups' ) . '</a>' .
|
||||
'</div>' .
|
||||
'</div>' . // .capability.edit
|
||||
'</form>' .
|
||||
'</div>'; // .manage-capabilities
|
||||
|
||||
echo $output;
|
||||
} // function groups_admin_capabilities_edit
|
||||
|
||||
/**
|
||||
* Handle edit form submission.
|
||||
*/
|
||||
function groups_admin_capabilities_edit_submit() {
|
||||
|
||||
$result = false;
|
||||
|
||||
if ( !current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
|
||||
wp_die( __( 'Access denied.', 'groups' ) );
|
||||
}
|
||||
|
||||
if ( !wp_verify_nonce( $_POST[GROUPS_ADMIN_GROUPS_NONCE], 'capabilities-edit' ) ) {
|
||||
wp_die( __( 'Access denied.', 'groups' ) );
|
||||
}
|
||||
|
||||
$capability_id = isset( $_POST['capability-id-field'] ) ? $_POST['capability-id-field'] : null;
|
||||
$capability = Groups_Capability::read( $capability_id );
|
||||
if ( $capability ) {
|
||||
$capability_id = $capability->capability_id;
|
||||
if ( $capability->capability !== Groups_Post_Access::READ_POST_CAPABILITY ) {
|
||||
$capability_field = isset( $_POST['capability-field'] ) ? $_POST['capability-field'] : null;
|
||||
} else {
|
||||
$capability_field = Groups_Post_Access::READ_POST_CAPABILITY;
|
||||
}
|
||||
if ( !empty( $capability_field ) ) {
|
||||
$update = true;
|
||||
if ( $other_capability = Groups_Capability::read_by_capability( $capability_field ) ) {
|
||||
if ( $other_capability->capability_id != $capability_id ) {
|
||||
Groups_Admin::add_message( sprintf( __( 'The <em>%s</em> capability already exists and cannot be assigned to this one.', 'groups' ), stripslashes( wp_filter_nohtml_kses( $other_capability->capability ) ) ), 'error' );
|
||||
$update = false;
|
||||
}
|
||||
}
|
||||
if ( $update ) {
|
||||
$description = isset( $_POST['description-field'] ) ? $_POST['description-field'] : '';
|
||||
$capability_id = Groups_Capability::update( array( 'capability_id' => $capability_id, 'capability' => $capability_field, 'description' => $description ) );
|
||||
if ( $capability_id ) {
|
||||
$result = $capability_id;
|
||||
} else {
|
||||
Groups_Admin::add_message( sprintf( __( 'The <em>%s</em> capability could not be updated.', 'groups' ), stripslashes( wp_filter_nohtml_kses( $capability ) ) ), 'error' );
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Groups_Admin::add_message( __( 'The <em>Capability</em> must not be empty.', 'groups' ), 'error' );
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
} // function groups_admin_capabilities_edit_submit
|
||||
@@ -0,0 +1,198 @@
|
||||
<?php
|
||||
/**
|
||||
* groups-admin-capabilities-remove.php
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.0.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows form to confirm capability deletion.
|
||||
* @param int $capability_id capability id
|
||||
*/
|
||||
function groups_admin_capabilities_remove( $capability_id ) {
|
||||
|
||||
global $wpdb;
|
||||
|
||||
if ( !current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
|
||||
wp_die( __( 'Access denied.', 'groups' ) );
|
||||
}
|
||||
|
||||
$capability = Groups_Capability::read( intval( $capability_id ) );
|
||||
|
||||
if ( empty( $capability ) ) {
|
||||
wp_die( __( 'No such capability.', 'groups' ) );
|
||||
}
|
||||
|
||||
$capability_table = _groups_get_tablename( 'capability' );
|
||||
|
||||
$current_url = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
|
||||
$current_url = remove_query_arg( 'action', $current_url );
|
||||
$current_url = remove_query_arg( 'capability_id', $current_url );
|
||||
|
||||
$output =
|
||||
'<div class="manage-capabilities wrap">' .
|
||||
'<h1>' .
|
||||
__( 'Remove a capability', 'groups' ) .
|
||||
'</h1>' .
|
||||
'<form id="remove-capability" action="' . esc_url( $current_url ) . '" method="post">' .
|
||||
'<div class="capability remove">' .
|
||||
'<input id="capability-id-field" name="capability-id-field" type="hidden" value="' . esc_attr( intval( $capability->capability_id ) ) . '"/>' .
|
||||
'<ul>' .
|
||||
'<li>' . sprintf( __( 'Capability : %s', 'groups' ), stripslashes( wp_filter_nohtml_kses( $capability->capability ) ) ) . '</li>' .
|
||||
'</ul> ' .
|
||||
wp_nonce_field( 'capabilities-remove', GROUPS_ADMIN_GROUPS_NONCE, true, false ) .
|
||||
'<input class="button button-primary" type="submit" value="' . __( 'Remove', 'groups' ) . '"/>' .
|
||||
'<input type="hidden" value="remove" name="action"/>' .
|
||||
'<a class="cancel button" href="' . esc_url( $current_url ) . '">' . __( 'Cancel', 'groups' ) . '</a>' .
|
||||
'</div>' .
|
||||
'</div>' . // .capability.remove
|
||||
'</form>' .
|
||||
'</div>'; // .manage-capabilities
|
||||
|
||||
echo $output;
|
||||
} // function groups_admin_capabilities_remove
|
||||
|
||||
/**
|
||||
* Handle remove form submission.
|
||||
*/
|
||||
function groups_admin_capabilities_remove_submit() {
|
||||
|
||||
global $wpdb;
|
||||
|
||||
$result = false;
|
||||
|
||||
if ( !current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
|
||||
wp_die( __( 'Access denied.', 'groups' ) );
|
||||
}
|
||||
|
||||
if ( !wp_verify_nonce( $_POST[GROUPS_ADMIN_GROUPS_NONCE], 'capabilities-remove' ) ) {
|
||||
wp_die( __( 'Access denied.', 'groups' ) );
|
||||
}
|
||||
|
||||
$capability_id = isset( $_POST['capability-id-field'] ) ? $_POST['capability-id-field'] : null;
|
||||
$capability = Groups_Capability::read( $capability_id );
|
||||
if ( $capability ) {
|
||||
if ( $capability->capability !== Groups_Post_Access::READ_POST_CAPABILITY ) {
|
||||
$result = Groups_Capability::delete( $capability_id );
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
} // function groups_admin_capabilities_remove_submit
|
||||
|
||||
/**
|
||||
* Shows form to confirm removal bulk capabilities
|
||||
*/
|
||||
function groups_admin_capabilities_bulk_remove() {
|
||||
|
||||
global $wpdb;
|
||||
|
||||
$output = '';
|
||||
|
||||
if ( !current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
|
||||
wp_die( __( 'Access denied.', 'groups' ) );
|
||||
}
|
||||
|
||||
$capability_ids = isset( $_POST['capability_ids'] ) ? $_POST['capability_ids'] : null;
|
||||
|
||||
if ( ! $capability_ids ) {
|
||||
wp_die( __( 'No such capabilities.', 'groups' ) );
|
||||
}
|
||||
|
||||
$capabilities = array();
|
||||
foreach ( $capability_ids as $capability_id ) {
|
||||
$capability = Groups_Capability::read( intval( $capability_id ) );
|
||||
if ( $capability ) {
|
||||
$capabilities[] = $capability;
|
||||
}
|
||||
}
|
||||
|
||||
$current_url = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
|
||||
$current_url = remove_query_arg( 'action', $current_url );
|
||||
$current_url = remove_query_arg( 'capability_id', $current_url );
|
||||
|
||||
$output .= '<div class="manage-capabilities wrap">';
|
||||
$output .= '<h1>';
|
||||
$output .= __( 'Remove capabilities', 'groups' );
|
||||
$output .= '</h1>';
|
||||
|
||||
$output .= '<form id="capabilities-action" method="post" action="">';
|
||||
$output .= '<div class="capability remove">';
|
||||
$output .= '<p>';
|
||||
$output .= __( 'Please confirm to remove the following capabilities. This action cannot be undone.', 'groups' );
|
||||
$output .= '</p>';
|
||||
foreach ( $capabilities as $capability ) {
|
||||
$output .= '<input id="capability_ids" name="capability_ids[]" type="hidden" value="' . esc_attr( intval( $capability->capability_id ) ) . '"/>';
|
||||
$output .= '<ul>';
|
||||
$output .= '<li>';
|
||||
$output .= sprintf( __( '<strong>%s</strong>', 'groups' ), wp_filter_nohtml_kses( $capability->capability ) );
|
||||
$output .= '</li>';
|
||||
$output .= '</ul>';
|
||||
}
|
||||
$output .= '<input class="button button-primary" type="submit" name="bulk" value="' . __( "Remove", 'groups' ) . '"/>';
|
||||
$output .= '<a class="cancel button" href="' . esc_url( $current_url ) . '">' . __( 'Cancel', 'groups' ) . '</a>';
|
||||
|
||||
$output .= '<input type="hidden" name="action" value="groups-action"/>';
|
||||
$output .= '<input type="hidden" name="bulk-action" value="remove"/>';
|
||||
$output .= '<input type="hidden" name="confirm" value="1"/>';
|
||||
$output .= wp_nonce_field( 'admin', GROUPS_ADMIN_GROUPS_ACTION_NONCE, true, false );
|
||||
|
||||
$output .= '</div>';
|
||||
$output .= '</form>';
|
||||
$output .= '</div>';
|
||||
|
||||
echo $output;
|
||||
} // function groups_admin_capabilities_bulk_remove
|
||||
|
||||
/**
|
||||
* Handle remove form submission.
|
||||
* @return array of deleted capabilities' ids
|
||||
*/
|
||||
function groups_admin_capabilities_bulk_remove_submit() {
|
||||
|
||||
global $wpdb;
|
||||
|
||||
$result = array();
|
||||
|
||||
if ( !current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
|
||||
wp_die( __( 'Access denied.', 'groups' ) );
|
||||
}
|
||||
|
||||
if ( !wp_verify_nonce( $_POST[GROUPS_ADMIN_GROUPS_ACTION_NONCE], 'admin' ) ) {
|
||||
wp_die( __( 'Access denied.', 'groups' ) );
|
||||
}
|
||||
|
||||
$capability_ids = isset( $_POST['capability_ids'] ) ? $_POST['capability_ids'] : null;
|
||||
|
||||
if ( $capability_ids ) {
|
||||
foreach ( $capability_ids as $capability_id ) {
|
||||
$capability = Groups_Capability::read( $capability_id );
|
||||
if ( $capability ) {
|
||||
if ( $capability->capability !== Groups_Post_Access::READ_POST_CAPABILITY ) {
|
||||
if ( Groups_Capability::delete( $capability_id ) ) {
|
||||
$result[] = $capability->capability_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
} // function groups_admin_capabilities_bulk_remove_submit
|
||||
@@ -0,0 +1,506 @@
|
||||
<?php
|
||||
/**
|
||||
* groups-admin-capabilities.php
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.0.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
define( 'GROUPS_CAPABILITIES_PER_PAGE', 10 );
|
||||
define( 'GROUPS_ADMIN_CAPABILITIES_NONCE_1', 'groups-cap-nonce-1');
|
||||
define( 'GROUPS_ADMIN_CAPABILITIES_NONCE_2', 'groups-cap-nonce-2');
|
||||
define( 'GROUPS_ADMIN_CAPABILITIES_ACTION_NONCE', 'groups-cap-action-nonce');
|
||||
define( 'GROUPS_ADMIN_CAPABILITIES_FILTER_NONCE', 'groups-cap-filter-nonce' );
|
||||
|
||||
require_once( GROUPS_CORE_LIB . '/class-groups-pagination.php' );
|
||||
require_once( GROUPS_ADMIN_LIB . '/groups-admin-capabilities-add.php');
|
||||
require_once( GROUPS_ADMIN_LIB . '/groups-admin-capabilities-edit.php');
|
||||
require_once( GROUPS_ADMIN_LIB . '/groups-admin-capabilities-remove.php');
|
||||
|
||||
/**
|
||||
* Manage capabilities: table of capabilities and add, edit, remove actions.
|
||||
*/
|
||||
function groups_admin_capabilities() {
|
||||
|
||||
global $wpdb;
|
||||
|
||||
$output = '';
|
||||
$today = date( 'Y-m-d', time() );
|
||||
|
||||
if ( !current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
|
||||
wp_die( __( 'Access denied.', 'groups' ) );
|
||||
}
|
||||
|
||||
//
|
||||
// handle actions
|
||||
//
|
||||
if ( isset( $_POST['action'] ) ) {
|
||||
// handle action submit - do it
|
||||
switch( $_POST['action'] ) {
|
||||
case 'add' :
|
||||
if ( !( $capability_id = groups_admin_capabilities_add_submit() ) ) {
|
||||
return groups_admin_capabilities_add();
|
||||
} else {
|
||||
$capability = Groups_Capability::read( $capability_id );
|
||||
Groups_Admin::add_message( sprintf( __( 'The <em>%s</em> capability has been created.', 'groups' ), stripslashes( wp_filter_nohtml_kses( $capability->capability ) ) ) );
|
||||
}
|
||||
break;
|
||||
case 'edit' :
|
||||
if ( !( $capability_id = groups_admin_capabilities_edit_submit() ) ) {
|
||||
return groups_admin_capabilities_edit( $_POST['capability-id-field'] );
|
||||
} else {
|
||||
$capability = Groups_Capability::read( $capability_id );
|
||||
Groups_Admin::add_message( sprintf( __( 'The <em>%s</em> capability has been updated.', 'groups' ), stripslashes( wp_filter_nohtml_kses( $capability->capability ) ) ) );
|
||||
}
|
||||
break;
|
||||
case 'remove' :
|
||||
if ( $capability_id = groups_admin_capabilities_remove_submit() ) {
|
||||
Groups_Admin::add_message( __( 'The capability has been deleted.', 'groups' ) );
|
||||
}
|
||||
break;
|
||||
// bulk actions on groups: capabilities
|
||||
case 'groups-action' :
|
||||
if ( wp_verify_nonce( $_POST[GROUPS_ADMIN_GROUPS_ACTION_NONCE], 'admin' ) ) {
|
||||
$capability_ids = isset( $_POST['capability_ids'] ) ? $_POST['capability_ids'] : null;
|
||||
$bulk = isset( $_POST['bulk'] ) ? $_POST['bulk'] : null;
|
||||
if ( is_array( $capability_ids ) && ( $bulk !== null ) ) {
|
||||
foreach ( $capability_ids as $capability_id ) {
|
||||
$bulk_action = isset( $_POST['bulk-action'] ) ? $_POST['bulk-action'] : null;
|
||||
switch( $bulk_action ) {
|
||||
case 'remove' :
|
||||
if ( isset( $_POST['confirm'] ) ) {
|
||||
groups_admin_capabilities_bulk_remove_submit();
|
||||
} else {
|
||||
return groups_admin_capabilities_bulk_remove();
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else if ( isset ( $_GET['action'] ) ) {
|
||||
// handle action request - show form
|
||||
switch( $_GET['action'] ) {
|
||||
case 'add' :
|
||||
return groups_admin_capabilities_add();
|
||||
break;
|
||||
case 'edit' :
|
||||
if ( isset( $_GET['capability_id'] ) ) {
|
||||
return groups_admin_capabilities_edit( $_GET['capability_id'] );
|
||||
}
|
||||
break;
|
||||
case 'remove' :
|
||||
if ( isset( $_GET['capability_id'] ) ) {
|
||||
return groups_admin_capabilities_remove( $_GET['capability_id'] );
|
||||
}
|
||||
break;
|
||||
case 'refresh' :
|
||||
if ( check_admin_referer( 'refresh' ) ) {
|
||||
$n = Groups_WordPress::refresh_capabilities();
|
||||
if ( $n > 0 ) {
|
||||
$output .= '<div class="updated fade"><p>' . sprintf( _n( 'One capability has been added.', '%d capabilities have been added.', $n, 'groups' ), $n ) . '</p></div>';
|
||||
} else {
|
||||
$output .= '<div class="updated fade"><p>' . __( 'No new capabilities have been found.', 'groups' ) . '</p></div>';
|
||||
}
|
||||
} else {
|
||||
wp_die( __( 'A Duck!', 'groups' ) );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// capabilities table
|
||||
//
|
||||
if (
|
||||
isset( $_POST['clear_filters'] ) ||
|
||||
isset( $_POST['capability_id'] ) ||
|
||||
isset( $_POST['capability'] )
|
||||
) {
|
||||
if ( !wp_verify_nonce( $_POST[GROUPS_ADMIN_CAPABILITIES_FILTER_NONCE], 'admin' ) ) {
|
||||
wp_die( __( 'Access denied.', 'groups' ) );
|
||||
}
|
||||
}
|
||||
|
||||
// filters
|
||||
$capability_id = Groups_Options::get_user_option( 'capabilities_capability_id', null );
|
||||
$capability = Groups_Options::get_user_option( 'capabilities_capability', null );
|
||||
|
||||
if ( isset( $_POST['clear_filters'] ) ) {
|
||||
Groups_Options::delete_user_option( 'capabilities_capability_id' );
|
||||
Groups_Options::delete_user_option( 'capabilities_capability' );
|
||||
$capability_id = null;
|
||||
$capability = null;
|
||||
} else if ( isset( $_POST['submitted'] ) ) {
|
||||
// filter by name
|
||||
if ( !empty( $_POST['capability'] ) ) {
|
||||
$capability = $_POST['capability'];
|
||||
Groups_Options::update_user_option( 'capabilities_capability', $capability );
|
||||
}
|
||||
// filter by capability id
|
||||
if ( !empty( $_POST['capability_id'] ) ) {
|
||||
$capability_id = intval( $_POST['capability_id'] );
|
||||
Groups_Options::update_user_option( 'capabilities_capability_id', $capability_id );
|
||||
} else if ( isset( $_POST['capability_id'] ) ) { // empty && isset => '' => all
|
||||
$capability_id = null;
|
||||
Groups_Options::delete_user_option( 'capabilities_capability_id' );
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $_POST['row_count'] ) ) {
|
||||
if ( !wp_verify_nonce( $_POST[GROUPS_ADMIN_CAPABILITIES_NONCE_1], 'admin' ) ) {
|
||||
wp_die( __( 'Access denied.', 'groups' ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $_POST['paged'] ) ) {
|
||||
if ( !wp_verify_nonce( $_POST[GROUPS_ADMIN_CAPABILITIES_NONCE_2], 'admin' ) ) {
|
||||
wp_die( __( 'Access denied.', 'groups' ) );
|
||||
}
|
||||
}
|
||||
|
||||
$current_url = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
|
||||
$current_url = remove_query_arg( 'paged', $current_url );
|
||||
$current_url = remove_query_arg( 'action', $current_url );
|
||||
$current_url = remove_query_arg( 'capability_id', $current_url );
|
||||
|
||||
$capability_table = _groups_get_tablename( 'capability' );
|
||||
|
||||
$output .=
|
||||
'<div class="manage-capabilities wrap">' .
|
||||
'<h1>' .
|
||||
__( 'Capabilities', 'groups' ) .
|
||||
// add capability
|
||||
sprintf(
|
||||
'<a title="%s" class="add page-title-action" href="%s">',
|
||||
esc_attr( __( 'Click to add a new capability', 'groups' ) ),
|
||||
esc_url( $current_url . '&action=add' )
|
||||
) .
|
||||
sprintf(
|
||||
'<img class="icon" alt="%s" src="%s" />',
|
||||
esc_attr( __( 'Add', 'groups' ) ),
|
||||
esc_url( GROUPS_PLUGIN_URL . 'images/add.png' )
|
||||
) .
|
||||
sprintf(
|
||||
'<span class="label">%s</span>',
|
||||
stripslashes( wp_filter_nohtml_kses( __( 'New Capability', 'groups' ) ) )
|
||||
) .
|
||||
'</a>' .
|
||||
// refresh capabilities
|
||||
sprintf(
|
||||
'<a title="%s" class="refresh page-title-action" href="%s">',
|
||||
esc_attr( __( 'Click to refresh capabilities', 'groups' ) ),
|
||||
esc_url( wp_nonce_url( $current_url . '&action=refresh', 'refresh' ) )
|
||||
) .
|
||||
sprintf(
|
||||
'<img class="icon" alt="%s" src="%s" />',
|
||||
esc_attr( __( 'Refresh', 'groups' ) ),
|
||||
esc_url( GROUPS_PLUGIN_URL . 'images/refresh.png' )
|
||||
) .
|
||||
sprintf(
|
||||
'<span class="label">%s</span>',
|
||||
stripslashes( wp_filter_nohtml_kses( __( 'Refresh', 'groups' ) ) )
|
||||
) .
|
||||
'</a>' .
|
||||
'</h1>';
|
||||
|
||||
$output .= Groups_Admin::render_messages();
|
||||
|
||||
$row_count = isset( $_POST['row_count'] ) ? intval( $_POST['row_count'] ) : 0;
|
||||
|
||||
if ($row_count <= 0) {
|
||||
$row_count = Groups_Options::get_user_option( 'capabilities_per_page', GROUPS_CAPABILITIES_PER_PAGE );
|
||||
} else {
|
||||
Groups_Options::update_user_option('capabilities_per_page', $row_count );
|
||||
}
|
||||
$offset = isset( $_GET['offset'] ) ? intval( $_GET['offset'] ) : 0;
|
||||
if ( $offset < 0 ) {
|
||||
$offset = 0;
|
||||
}
|
||||
$paged = isset( $_REQUEST['paged'] ) ? intval( $_REQUEST['paged'] ) : 0;
|
||||
if ( $paged < 0 ) {
|
||||
$paged = 0;
|
||||
}
|
||||
|
||||
$orderby = isset( $_GET['orderby'] ) ? $_GET['orderby'] : null;
|
||||
switch ( $orderby ) {
|
||||
case 'capability_id' :
|
||||
case 'capability' :
|
||||
case 'description' :
|
||||
break;
|
||||
default:
|
||||
$orderby = 'name';
|
||||
}
|
||||
|
||||
$order = isset( $_GET['order'] ) ? $_GET['order'] : null;
|
||||
switch ( $order ) {
|
||||
case 'asc' :
|
||||
case 'ASC' :
|
||||
$switch_order = 'DESC';
|
||||
break;
|
||||
case 'desc' :
|
||||
case 'DESC' :
|
||||
$switch_order = 'ASC';
|
||||
break;
|
||||
default:
|
||||
$order = 'ASC';
|
||||
$switch_order = 'DESC';
|
||||
}
|
||||
|
||||
$filters = array( " 1=%d " );
|
||||
$filter_params = array( 1 );
|
||||
if ( $capability_id ) {
|
||||
$filters[] = " $capability_table.capability_id = %d ";
|
||||
$filter_params[] = $capability_id;
|
||||
}
|
||||
if ( $capability ) {
|
||||
$filters[] = " $capability_table.capability LIKE '%%%s%%' ";
|
||||
$filter_params[] = $capability;
|
||||
}
|
||||
|
||||
if ( !empty( $filters ) ) {
|
||||
$filters = " WHERE " . implode( " AND ", $filters );
|
||||
} else {
|
||||
$filters = '';
|
||||
}
|
||||
|
||||
$count_query = $wpdb->prepare( "SELECT COUNT(*) FROM $capability_table $filters", $filter_params );
|
||||
$count = $wpdb->get_var( $count_query );
|
||||
if ( $count > $row_count ) {
|
||||
$paginate = true;
|
||||
} else {
|
||||
$paginate = false;
|
||||
}
|
||||
$pages = ceil ( $count / $row_count );
|
||||
if ( $paged > $pages ) {
|
||||
$paged = $pages;
|
||||
}
|
||||
if ( $paged != 0 ) {
|
||||
$offset = ( $paged - 1 ) * $row_count;
|
||||
}
|
||||
|
||||
$query = $wpdb->prepare(
|
||||
"SELECT * FROM $capability_table
|
||||
$filters
|
||||
ORDER BY $orderby $order
|
||||
LIMIT $row_count OFFSET $offset",
|
||||
$filter_params
|
||||
);
|
||||
|
||||
$results = $wpdb->get_results( $query, OBJECT );
|
||||
|
||||
$column_display_names = array(
|
||||
'capability_id' => __( 'ID', 'groups' ),
|
||||
'capability' => __( 'Capability', 'groups' ),
|
||||
'description' => __( 'Description', 'groups' )
|
||||
);
|
||||
|
||||
$output .= '<div class="capabilities-overview">';
|
||||
|
||||
$output .=
|
||||
'<div class="filters">' .
|
||||
'<form id="setfilters" action="" method="post">' .
|
||||
'<fieldset>' .
|
||||
'<legend>' . __( 'Filters', 'groups' ) . '</legend>' .
|
||||
'<label class="capability-id-filter">' .
|
||||
__( 'Capability ID', 'groups' ) . ' ' .
|
||||
'<input class="capability-id-filter" name="capability_id" type="text" value="' . esc_attr( $capability_id ) . '"/>' .
|
||||
'</label>' . ' ' .
|
||||
'<label class="capability-filter">' .
|
||||
__( 'Capability', 'groups' ) . ' ' .
|
||||
'<input class="capability-filter" name="capability" type="text" value="' . $capability . '"/>' .
|
||||
'</label>' . ' ' .
|
||||
wp_nonce_field( 'admin', GROUPS_ADMIN_CAPABILITIES_FILTER_NONCE, true, false ) .
|
||||
'<input class="button" type="submit" value="' . __( 'Apply', 'groups' ) . '"/>' . ' ' .
|
||||
'<input class="button" type="submit" name="clear_filters" value="' . __( 'Clear', 'groups' ) . '"/>' .
|
||||
'<input type="hidden" value="submitted" name="submitted"/>' .
|
||||
'</fieldset>' .
|
||||
'</form>' .
|
||||
'</div>';
|
||||
|
||||
if ( $paginate ) {
|
||||
require_once( GROUPS_CORE_LIB . '/class-groups-pagination.php' );
|
||||
$pagination = new Groups_Pagination( $count, null, $row_count );
|
||||
$output .= '<form id="posts-filter" method="post" action="">';
|
||||
$output .= '<div>';
|
||||
$output .= wp_nonce_field( 'admin', GROUPS_ADMIN_CAPABILITIES_NONCE_2, true, false );
|
||||
$output .= '</div>';
|
||||
$output .= '<div class="tablenav top">';
|
||||
$output .= $pagination->pagination( 'top' );
|
||||
$output .= '</div>';
|
||||
$output .= '</form>';
|
||||
}
|
||||
|
||||
$output .= '<div class="page-options right">';
|
||||
$output .= '<form id="setrowcount" action="" method="post">';
|
||||
$output .= '<div>';
|
||||
$output .= '<label for="row_count">' . __( 'Results per page', 'groups' ) . '</label>';
|
||||
$output .= '<input name="row_count" type="text" size="2" value="' . esc_attr( $row_count ) .'" />';
|
||||
$output .= wp_nonce_field( 'admin', GROUPS_ADMIN_CAPABILITIES_NONCE_1, true, false );
|
||||
$output .= '<input class="button" type="submit" value="' . __( 'Apply', 'groups' ) . '"/>';
|
||||
$output .= '</div>';
|
||||
$output .= '</form>';
|
||||
$output .= '</div>';
|
||||
|
||||
$output .= '<form id="groups-action" method="post" action="">';
|
||||
|
||||
$output .= '<div class="tablenav top">';
|
||||
$output .= '<div class="capabilities-bulk-container">';
|
||||
$output .= '<div class="alignleft actions">';
|
||||
$output .= '<select name="bulk-action">';
|
||||
$output .= '<option selected="selected" value="-1">' . esc_html( __( 'Bulk Actions', 'groups' ) ) . '</option>';
|
||||
$output .= '<option value="remove">' . esc_html( __( 'Remove', 'groups' ) ) . '</option>';
|
||||
$output .= '</select>';
|
||||
$output .= '<input class="button" type="submit" name="bulk" value="' . esc_attr( __( "Apply", 'groups' ) ) . '"/>';
|
||||
$output .= '</div>';
|
||||
$output .= '</div>';
|
||||
$output .= '</div>';
|
||||
$output .= wp_nonce_field( 'admin', GROUPS_ADMIN_GROUPS_ACTION_NONCE, true, false );
|
||||
$output .= '<input type="hidden" name="action" value="groups-action"/>';
|
||||
|
||||
$output .= '<table id="" class="wp-list-table widefat fixed" cellspacing="0">';
|
||||
$output .= '<thead>';
|
||||
$output .= '<tr>';
|
||||
|
||||
$output .= '<th id="cb" class="manage-column column-cb check-column" scope="col"><input type="checkbox"></th>';
|
||||
|
||||
foreach ( $column_display_names as $key => $column_display_name ) {
|
||||
$options = array(
|
||||
'orderby' => $key,
|
||||
'order' => $switch_order
|
||||
);
|
||||
$class = $key;
|
||||
if ( !in_array($key, array( 'capabilities', 'edit', 'remove' ) ) ) {
|
||||
if ( strcmp( $key, $orderby ) == 0 ) {
|
||||
$lorder = strtolower( $order );
|
||||
$class = "$key manage-column sorted $lorder";
|
||||
} else {
|
||||
$class = "$key manage-column sortable";
|
||||
}
|
||||
$column_display_name =
|
||||
sprintf(
|
||||
'<a href="%s"><span>%s</span><span class="sorting-indicator"></span></a>',
|
||||
esc_url( add_query_arg( $options, $current_url ) ),
|
||||
esc_html( $column_display_name )
|
||||
);
|
||||
} else {
|
||||
$column_display_name = esc_html( $column_display_name );
|
||||
}
|
||||
$output .= sprintf(
|
||||
'<th scope="col" class="%s">%s</th>',
|
||||
esc_attr( $class ),
|
||||
$column_display_name
|
||||
);
|
||||
}
|
||||
|
||||
$output .= '</tr>';
|
||||
$output .= '</thead>';
|
||||
$output .= '<tbody>';
|
||||
|
||||
if ( count( $results ) > 0 ) {
|
||||
for ( $i = 0; $i < count( $results ); $i++ ) {
|
||||
$result = $results[$i];
|
||||
|
||||
// Construct the "edit" URL.
|
||||
$edit_url = add_query_arg(
|
||||
array(
|
||||
'capability_id' => intval( $result->capability_id ),
|
||||
'action' => 'edit',
|
||||
'paged' => $paged
|
||||
),
|
||||
$current_url
|
||||
);
|
||||
// Construct the "delete" URL.
|
||||
$delete_url = add_query_arg(
|
||||
array(
|
||||
'capability_id' => intval( $result->capability_id ),
|
||||
'action' => 'remove',
|
||||
'paged' => $paged
|
||||
),
|
||||
$current_url
|
||||
);
|
||||
|
||||
// Construct row actions for this group.
|
||||
$row_actions =
|
||||
'<div class="row-actions">' .
|
||||
'<span class="edit">' .
|
||||
'<a href="' . esc_url( $edit_url ) . '">' .
|
||||
'<img src="' . GROUPS_PLUGIN_URL . 'images/edit.png"/>' .
|
||||
__( 'Edit', 'groups' ) .
|
||||
'</a>';
|
||||
if ( $result->capability !== Groups_Post_Access::READ_POST_CAPABILITY ) {
|
||||
$row_actions .=
|
||||
' | '.
|
||||
'</span>' .
|
||||
'<span class="remove trash">' .
|
||||
'<a href="' . esc_url( $delete_url ) . '" class="submitdelete">' .
|
||||
'<img src="' . GROUPS_PLUGIN_URL . 'images/remove.png"/>' .
|
||||
__( 'Remove', 'groups' ) .
|
||||
'</a>' .
|
||||
'</span>';
|
||||
}
|
||||
$row_actions .= '</div>'; // .row-actions
|
||||
|
||||
$output .= '<tr class="' . ( $i % 2 == 0 ? 'even' : 'odd' ) . '">';
|
||||
|
||||
$output .= '<th class="check-column">';
|
||||
$output .= '<input type="checkbox" value="' . esc_attr( $result->capability_id ) . '" name="capability_ids[]"/>';
|
||||
$output .= '</th>';
|
||||
|
||||
$output .= '<td class="capability-id">';
|
||||
$output .= $result->capability_id;
|
||||
$output .= '</td>';
|
||||
$output .= '<td class="capability">';
|
||||
$output .= sprintf( '<a href="%s">%s</a>', esc_url( $edit_url ), stripslashes( wp_filter_nohtml_kses( $result->capability ) ) );
|
||||
$output .= $row_actions;
|
||||
$output .= '</td>';
|
||||
$output .= '<td class="description">';
|
||||
$output .= stripslashes( wp_filter_nohtml_kses( $result->description ) );
|
||||
$output .= '</td>';
|
||||
|
||||
$output .= '</tr>';
|
||||
}
|
||||
} else {
|
||||
$output .= '<tr><td colspan="3">' . __( 'There are no results.', 'groups' ) . '</td></tr>';
|
||||
}
|
||||
|
||||
$output .= '</tbody>';
|
||||
$output .= '</table>';
|
||||
|
||||
$output .= Groups_UIE::render_add_titles( '.capabilities-overview table td' );
|
||||
|
||||
$output .= '</form>'; // #groups-action
|
||||
|
||||
if ( $paginate ) {
|
||||
require_once( GROUPS_CORE_LIB . '/class-groups-pagination.php' );
|
||||
$pagination = new Groups_Pagination($count, null, $row_count);
|
||||
$output .= '<div class="tablenav bottom">';
|
||||
$output .= $pagination->pagination( 'bottom' );
|
||||
$output .= '</div>';
|
||||
}
|
||||
|
||||
$output .= '</div>'; // .capabilities-overview
|
||||
$output .= '</div>'; // .manage-capabilities
|
||||
|
||||
echo $output;
|
||||
} // function groups_admin_capabilities()
|
||||
@@ -0,0 +1,170 @@
|
||||
<?php
|
||||
/**
|
||||
* groups-admin-groups-add.php
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.1.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show add group form.
|
||||
*/
|
||||
function groups_admin_groups_add() {
|
||||
|
||||
global $wpdb;
|
||||
|
||||
$output = '';
|
||||
|
||||
if ( !current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
|
||||
wp_die( __( 'Access denied.', 'groups' ) );
|
||||
}
|
||||
|
||||
$current_url = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
|
||||
$current_url = remove_query_arg( 'paged', $current_url );
|
||||
$current_url = remove_query_arg( 'action', $current_url );
|
||||
$current_url = remove_query_arg( 'group_id', $current_url );
|
||||
|
||||
$parent_id = isset( $_POST['parent-id-field'] ) ? $_POST['parent-id-field'] : '';
|
||||
$name = isset( $_POST['name-field'] ) ? $_POST['name-field'] : '';
|
||||
$description = isset( $_POST['description-field'] ) ? $_POST['description-field'] : '';
|
||||
|
||||
$group_table = _groups_get_tablename( 'group' );
|
||||
$parent_select = '<select name="parent-id-field">';
|
||||
$parent_select .= '<option value="">--</option>';
|
||||
$groups = $wpdb->get_results( "SELECT * FROM $group_table" );
|
||||
foreach ( $groups as $group ) {
|
||||
$parent_select .= '<option value="' . esc_attr( $group->group_id ) . '">' . wp_filter_nohtml_kses( $group->name ) . '</option>';
|
||||
}
|
||||
$parent_select .= '</select>';
|
||||
|
||||
$output .= '<div class="manage-groups wrap">';
|
||||
$output .= '<h1>';
|
||||
$output .= __( 'Add a new group', 'groups' );
|
||||
$output .= '</h1>';
|
||||
|
||||
$output .= Groups_Admin::render_messages();
|
||||
|
||||
$output .= '<form id="add-group" action="' . esc_url( $current_url ) . '" method="post">';
|
||||
$output .= '<div class="group new">';
|
||||
|
||||
$output .= '<div class="field">';
|
||||
$output .= '<label for="name-field" class="field-label first required">';
|
||||
$output .= __( 'Name', 'groups' );
|
||||
$output .= '</label>';
|
||||
$output .= '<input id="name-field" name="name-field" class="namefield" type="text" value="' . esc_attr( stripslashes( $name ) ) . '"/>';
|
||||
$output .= '</div>';
|
||||
|
||||
$output .= '<div class="field">';
|
||||
$output .= '<label for="parent-id-field" class="field-label">';
|
||||
$output .= __( 'Parent', 'groups' );
|
||||
$output .= '</label>';
|
||||
$output .= $parent_select;
|
||||
$output .= '</div>';
|
||||
|
||||
$output .= '<div class="field">';
|
||||
$output .= '<label for="description-field" class="field-label description-field">';
|
||||
$output .= __( 'Description', 'groups' );
|
||||
$output .= '</label>';
|
||||
$output .= '<textarea id="description-field" name="description-field" rows="5" cols="45">';
|
||||
$output .= stripslashes( wp_filter_nohtml_kses( $description ) );
|
||||
$output .= '</textarea>';
|
||||
$output .= '</div>';
|
||||
|
||||
$output .= '<div class="field">';
|
||||
|
||||
$capability_table = _groups_get_tablename( "capability" );
|
||||
$capabilities = $wpdb->get_results( "SELECT * FROM $capability_table ORDER BY capability" );
|
||||
|
||||
$output .= '<div class="select-capability-container" style="width:62%;">';
|
||||
$output .= '<label>';
|
||||
$output .= __( 'Capabilities', 'groups' );
|
||||
$output .= sprintf(
|
||||
'<select class="select capability" name="capability_ids[]" multiple="multiple" placeholder="%s">',
|
||||
__( 'Choose capabilities …', 'groups' )
|
||||
);
|
||||
foreach( $capabilities as $capability ) {
|
||||
$output .= sprintf( '<option value="%s">%s</option>', esc_attr( $capability->capability_id ), wp_filter_nohtml_kses( $capability->capability ) );
|
||||
}
|
||||
$output .= '</select>';
|
||||
$output .= '</label>';
|
||||
$output .= '</div>';
|
||||
$output .= '<p class="description">';
|
||||
$output .= __( 'These capabilities will be assigned to the group.', 'groups' );
|
||||
$output .= '</p>';
|
||||
|
||||
$output .= Groups_UIE::render_select( '.select.capability' );
|
||||
$output .= '</div>';
|
||||
|
||||
$output .= apply_filters( 'groups_admin_groups_add_form_after_fields', '' );
|
||||
|
||||
$output .= '<div class="field">';
|
||||
$output .= wp_nonce_field( 'groups-add', GROUPS_ADMIN_GROUPS_NONCE, true, false );
|
||||
$output .= '<input class="button button-primary" type="submit" value="' . __( 'Add', 'groups' ) . '"/>';
|
||||
$output .= '<input type="hidden" value="add" name="action"/>';
|
||||
$output .= '<a class="cancel button" href="' . esc_url( $current_url ) . '">' . __( 'Cancel', 'groups' ) . '</a>';
|
||||
$output .= '</div>';
|
||||
$output .= '</div>'; // .group.new
|
||||
$output .= '</form>';
|
||||
$output .= '</div>'; // .manage-groups
|
||||
|
||||
echo $output;
|
||||
} // function groups_admin_groups_add
|
||||
|
||||
/**
|
||||
* Handle add group form submission.
|
||||
* @return int new group's id or false if unsuccessful
|
||||
*/
|
||||
function groups_admin_groups_add_submit() {
|
||||
|
||||
global $wpdb;
|
||||
|
||||
if ( !current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
|
||||
wp_die( __( 'Access denied.', 'groups' ) );
|
||||
}
|
||||
|
||||
if ( !wp_verify_nonce( $_POST[GROUPS_ADMIN_GROUPS_NONCE], 'groups-add' ) ) {
|
||||
wp_die( __( 'Access denied.', 'groups' ) );
|
||||
}
|
||||
|
||||
$creator_id = get_current_user_id();
|
||||
$datetime = date( 'Y-m-d H:i:s', time() );
|
||||
$parent_id = isset( $_POST['parent-id-field'] ) ? $_POST['parent-id-field'] : null;
|
||||
$description = isset( $_POST['description-field'] ) ? $_POST['description-field'] : '';
|
||||
$name = isset( $_POST['name-field'] ) ? $_POST['name-field'] : null;
|
||||
|
||||
$group_id = Groups_Group::create( compact( "creator_id", "datetime", "parent_id", "description", "name" ) );
|
||||
if ( $group_id ) {
|
||||
if ( !empty( $_POST['capability_ids'] ) ) {
|
||||
$caps = $_POST['capability_ids'];
|
||||
foreach( $caps as $cap ) {
|
||||
Groups_Group_Capability::create( array( 'group_id' => $group_id, 'capability_id' => $cap ) );
|
||||
}
|
||||
}
|
||||
do_action( 'groups_admin_groups_add_submit_success', $group_id );
|
||||
} else {
|
||||
if ( !$name ) {
|
||||
Groups_Admin::add_message( __( 'The name must not be empty.', 'groups' ), 'error' );
|
||||
} else if ( Groups_Group::read_by_name( $name ) ) {
|
||||
Groups_Admin::add_message( sprintf( __( 'The <em>%s</em> group already exists.', 'groups' ), stripslashes( wp_filter_nohtml_kses( ( $name ) ) ) ), 'error' );
|
||||
}
|
||||
}
|
||||
|
||||
return $group_id;
|
||||
} // function groups_admin_groups_add_submit
|
||||
@@ -0,0 +1,253 @@
|
||||
<?php
|
||||
/**
|
||||
* groups-admin-groups-edit.php
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.1.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show edit group form.
|
||||
* @param int $group_id group id
|
||||
*/
|
||||
function groups_admin_groups_edit( $group_id ) {
|
||||
|
||||
global $wpdb;
|
||||
|
||||
$output = '';
|
||||
|
||||
if ( !current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
|
||||
wp_die( __( 'Access denied.', 'groups' ) );
|
||||
}
|
||||
|
||||
$group = Groups_Group::read( intval( $group_id ) );
|
||||
|
||||
if ( empty( $group ) ) {
|
||||
wp_die( __( 'No such group.', 'groups' ) );
|
||||
}
|
||||
|
||||
$current_url = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
|
||||
$current_url = remove_query_arg( 'action', $current_url );
|
||||
$current_url = remove_query_arg( 'group_id', $current_url );
|
||||
|
||||
$name = isset( $_POST['name-field'] ) ? $_POST['name-field'] : $group->name;
|
||||
$description = isset( $_POST['description-field'] ) ? $_POST['description-field'] : $group->description;
|
||||
$parent_id = isset( $_POST['parent-id-field'] ) ? $_POST['parent-id-field'] : $group->parent_id;
|
||||
|
||||
$group_table = _groups_get_tablename( 'group' );
|
||||
$parent_select = '<select name="parent-id-field">';
|
||||
$parent_select .= '<option value="">--</option>';
|
||||
$groups = $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $group_table WHERE group_id != %d", $group->group_id ) );
|
||||
foreach ( $groups as $g ) {
|
||||
$selected = ( $g->group_id == $group->parent_id ? ' selected="selected" ' : '' );
|
||||
$parent_select .= '<option ' . $selected . 'value="' . esc_attr( $g->group_id ) . '">' . wp_filter_nohtml_kses( $g->name ) . '</option>';
|
||||
}
|
||||
$parent_select .= '</select>';
|
||||
|
||||
$name_readonly = ( $name !== Groups_Registered::REGISTERED_GROUP_NAME ) ? "" : ' readonly="readonly" ';
|
||||
|
||||
$output .= '<div class="manage-groups wrap">';
|
||||
$output .= '<h1>';
|
||||
$output .= __( 'Edit a group', 'groups' );
|
||||
$output .= '</h1>';
|
||||
|
||||
$output .= Groups_Admin::render_messages();
|
||||
|
||||
$output .= '<form id="edit-group" action="' . esc_url( $current_url ) . '" method="post">';
|
||||
$output .= '<div class="group edit">';
|
||||
$output .= '<input id="group-id-field" name="group-id-field" type="hidden" value="' . esc_attr( intval( $group_id ) ) . '"/>';
|
||||
|
||||
$output .= '<div class="field">';
|
||||
$output .= '<label for="name-field" class="field-label first required">';
|
||||
$output .= __( 'Name', 'groups' );
|
||||
$output .= '</label>';
|
||||
$output .= '<input ' . $name_readonly . ' id="name-field" name="name-field" class="namefield" type="text" value="' . esc_attr( stripslashes( $name ) ) . '"/>';
|
||||
$output .= '</div>';
|
||||
|
||||
$output .= '<div class="field">';
|
||||
$output .= '<label for="parent-id-field" class="field-label">';
|
||||
$output .= __( 'Parent', 'groups' );
|
||||
$output .= '</label>';
|
||||
$output .= $parent_select;
|
||||
$output .= '</div>';
|
||||
|
||||
$output .= '<div class="field">';
|
||||
$output .= '<label for="description-field" class="field-label description-field">';
|
||||
$output .= __( 'Description', 'groups' );
|
||||
$output .= '</label>';
|
||||
$output .= '<textarea id="description-field" name="description-field" rows="5" cols="45">';
|
||||
$output .= stripslashes( wp_filter_nohtml_kses( $description ) );
|
||||
$output .= '</textarea>';
|
||||
$output .= '</div>';
|
||||
|
||||
$capability_table = _groups_get_tablename( 'capability' );
|
||||
$group_capability_table = _groups_get_tablename( 'group_capability' );
|
||||
$group_capabilities = $wpdb->get_results( $wpdb->prepare(
|
||||
"SELECT * FROM $capability_table WHERE capability_id IN ( SELECT capability_id FROM $group_capability_table WHERE group_id = %d )",
|
||||
Groups_Utility::id( $group_id )
|
||||
) );
|
||||
$group_capabilities_array = array();
|
||||
if ( count( $group_capabilities ) > 0 ) {
|
||||
foreach ( $group_capabilities as $group_capability ) {
|
||||
$group_capabilities_array[] = $group_capability->capability_id;
|
||||
}
|
||||
}
|
||||
$capabilities = $wpdb->get_results( "SELECT * FROM $capability_table ORDER BY capability" );
|
||||
|
||||
$output .= '<div class="field">';
|
||||
$output .= '<div class="select-capability-container" style="width:62%;">';
|
||||
$output .= '<label>';
|
||||
$output .= __( 'Capabilities', 'groups' );
|
||||
$output .= sprintf(
|
||||
'<select class="select capability" name="capability_ids[]" multiple="multiple" placeholder="%s">',
|
||||
__( 'Choose capabilities …', 'groups' )
|
||||
);
|
||||
foreach( $capabilities as $capability ) {
|
||||
$selected = in_array( $capability->capability_id, $group_capabilities_array ) ? ' selected="selected" ' : '';
|
||||
$output .= sprintf( '<option value="%s" %s>%s</option>', esc_attr( $capability->capability_id ), $selected, wp_filter_nohtml_kses( $capability->capability ) );
|
||||
}
|
||||
$output .= '</select>';
|
||||
$output .= '</label>';
|
||||
$output .= '</div>'; // .select-capability-container
|
||||
$output .= '<p class="description">';
|
||||
$output .= __( 'The chosen capabilities are assigned to the group.', 'groups' );
|
||||
$output .= '</p>';
|
||||
$output .= '</div>'; // .field
|
||||
$output .= Groups_UIE::render_select( '.select.capability' );
|
||||
|
||||
$group_object = new Groups_Group( $group_id );
|
||||
$group_capabilities = $group_object->capabilities;
|
||||
$group_capabilities_deep = $group_object->capabilities_deep;
|
||||
if (
|
||||
(
|
||||
( !empty( $group_capabilities_deep ) ? count( $group_capabilities_deep ) : 0 ) -
|
||||
( !empty( $group_capabilities ) ? count( $group_capabilities ) : 0 )
|
||||
) > 0
|
||||
) {
|
||||
usort( $group_capabilities_deep, array( 'Groups_Utility', 'cmp' ) );
|
||||
$output .= '<div class="field">';
|
||||
$output .= __( 'Inherited capabilities:', 'groups' );
|
||||
$output .= ' ';
|
||||
$inherited_caps = array();
|
||||
foreach ( $group_capabilities_deep as $group_capability ) {
|
||||
$class = '';
|
||||
if ( empty( $group_capabilities ) || !in_array( $group_capability, $group_capabilities ) ) {
|
||||
$inherited_caps[] = wp_filter_nohtml_kses( $group_capability->capability->capability );
|
||||
}
|
||||
}
|
||||
$output .= implode( ' ', $inherited_caps );
|
||||
$output .= '</div>';
|
||||
}
|
||||
|
||||
$output .= apply_filters( 'groups_admin_groups_edit_form_after_fields', '', $group_id );
|
||||
|
||||
$output .= '<div class="field">';
|
||||
$output .= wp_nonce_field( 'groups-edit', GROUPS_ADMIN_GROUPS_NONCE, true, false );
|
||||
$output .= '<input class="button button-primary" type="submit" value="' . __( 'Save', 'groups' ) . '"/>';
|
||||
$output .= '<input type="hidden" value="edit" name="action"/>';
|
||||
$output .= '<a class="cancel button" href="' . esc_url( $current_url ) . '">' . __( 'Cancel', 'groups' ) . '</a>';
|
||||
$output .= '</div>';
|
||||
$output .= '</div>'; // .group.edit
|
||||
$output .= '</form>';
|
||||
$output .= '</div>'; // .manage-groups
|
||||
|
||||
echo $output;
|
||||
} // function groups_admin_groups_edit
|
||||
|
||||
/**
|
||||
* Handle edit form submission.
|
||||
*/
|
||||
function groups_admin_groups_edit_submit() {
|
||||
global $wpdb;
|
||||
|
||||
if ( !current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
|
||||
wp_die( __( 'Access denied.', 'groups' ) );
|
||||
}
|
||||
|
||||
if ( !wp_verify_nonce( $_POST[GROUPS_ADMIN_GROUPS_NONCE], 'groups-edit' ) ) {
|
||||
wp_die( __( 'Access denied.', 'groups' ) );
|
||||
}
|
||||
|
||||
$group_id = isset( $_POST['group-id-field'] ) ? $_POST['group-id-field'] : null;
|
||||
$group = Groups_Group::read( $group_id );
|
||||
if ( $group ) {
|
||||
$group_id = $group->group_id;
|
||||
if ( $group->name !== Groups_Registered::REGISTERED_GROUP_NAME ) {
|
||||
$name = isset( $_POST['name-field'] ) ? $_POST['name-field'] : null;
|
||||
} else {
|
||||
$name = Groups_Registered::REGISTERED_GROUP_NAME;
|
||||
}
|
||||
$parent_id = isset( $_POST['parent-id-field'] ) ? $_POST['parent-id-field'] : null;
|
||||
$description = isset( $_POST['description-field'] ) ? $_POST['description-field'] : '';
|
||||
|
||||
if ( empty( $name ) ) {
|
||||
Groups_Admin::add_message( __( 'The <em>Name</em> must not be empty.', 'groups' ), 'error' );
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( $other_group = Groups_Group::read_by_name( $name ) ) {
|
||||
if ( $other_group->group_id != $group_id ) {
|
||||
Groups_Admin::add_message(
|
||||
sprintf(
|
||||
__( 'The <em>%s</em> group already exists and cannot be used to name this one.', 'groups' ), stripslashes( wp_filter_nohtml_kses( $other_group->name ) )
|
||||
),
|
||||
'error'
|
||||
);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$group_id = Groups_Group::update( compact( "group_id", "name", "parent_id", "description" ) );
|
||||
if ( $group_id ) {
|
||||
$capability_table = _groups_get_tablename( "capability" );
|
||||
$group_capability_table = _groups_get_tablename( "group_capability" );
|
||||
$group_capabilities = $wpdb->get_results( $wpdb->prepare(
|
||||
"SELECT * FROM $capability_table WHERE capability_id IN ( SELECT capability_id FROM $group_capability_table WHERE group_id = %d )",
|
||||
Groups_Utility::id( $group_id )
|
||||
) );
|
||||
$group_capabilities_array = array();
|
||||
foreach ( $group_capabilities as $group_capability ) {
|
||||
$group_capabilities_array[] = $group_capability->capability_id;
|
||||
}
|
||||
|
||||
$caps = array();
|
||||
if ( isset( $_POST['capability_ids'] ) ) {
|
||||
$caps = $_POST['capability_ids'];
|
||||
}
|
||||
// delete
|
||||
foreach( $group_capabilities_array as $group_cap ) {
|
||||
if ( !in_array( $group_cap, $caps ) ) {
|
||||
Groups_Group_Capability::delete( $group_id, $group_cap );
|
||||
}
|
||||
}
|
||||
// add
|
||||
foreach( $caps as $cap ) {
|
||||
if ( !in_array( $cap, $group_capabilities_array ) ) {
|
||||
Groups_Group_Capability::create( array( 'group_id' => $group_id, 'capability_id' => $cap ) );
|
||||
}
|
||||
}
|
||||
do_action( 'groups_admin_groups_edit_submit_success', $group_id );
|
||||
}
|
||||
return $group_id;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
} // function groups_admin_groups_edit_submit
|
||||
@@ -0,0 +1,199 @@
|
||||
<?php
|
||||
/**
|
||||
* groups-admin-groups-remove.php
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.1.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows form to confirm removal of a group.
|
||||
* @param int $group_id group id
|
||||
*/
|
||||
function groups_admin_groups_remove( $group_id ) {
|
||||
|
||||
global $wpdb;
|
||||
|
||||
if ( !current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
|
||||
wp_die( __( 'Access denied.', 'groups' ) );
|
||||
}
|
||||
|
||||
$group = Groups_Group::read( intval( $group_id ) );
|
||||
|
||||
if ( empty( $group ) ) {
|
||||
wp_die( __( 'No such group.', 'groups' ) );
|
||||
}
|
||||
|
||||
$group_table = _groups_get_tablename( 'group' );
|
||||
|
||||
$current_url = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
|
||||
$current_url = remove_query_arg( 'action', $current_url );
|
||||
$current_url = remove_query_arg( 'group_id', $current_url );
|
||||
|
||||
$output =
|
||||
'<div class="manage-groups wrap">' .
|
||||
'<h1>' .
|
||||
__( 'Remove a group', 'groups' ) .
|
||||
'</h1>' .
|
||||
'<form id="remove-group" action="' . esc_url( $current_url ) . '" method="post">' .
|
||||
'<div class="group remove">' .
|
||||
'<input id="group-id-field" name="group-id-field" type="hidden" value="' . esc_attr( intval( $group->group_id ) ) . '"/>' .
|
||||
'<ul>' .
|
||||
'<li>' . sprintf( __( 'Group Name : %s', 'groups' ), stripslashes( wp_filter_nohtml_kses( $group->name ) ) ) . '</li>' .
|
||||
'</ul> ' .
|
||||
wp_nonce_field( 'groups-remove', GROUPS_ADMIN_GROUPS_NONCE, true, false ) .
|
||||
'<input class="button button-primary" type="submit" value="' . __( 'Remove', 'groups' ) . '"/>' .
|
||||
'<input type="hidden" value="remove" name="action"/>' .
|
||||
'<a class="cancel button" href="' . esc_url( $current_url ) . '">' . __( 'Cancel', 'groups' ) . '</a>' .
|
||||
'</div>' .
|
||||
'</div>' . // .group.remove
|
||||
'</form>' .
|
||||
'</div>'; // .manage-groups
|
||||
|
||||
echo $output;
|
||||
} // function groups_admin_groups_remove
|
||||
|
||||
/**
|
||||
* Handle remove form submission.
|
||||
*/
|
||||
function groups_admin_groups_remove_submit() {
|
||||
|
||||
global $wpdb;
|
||||
|
||||
$result = false;
|
||||
|
||||
if ( !current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
|
||||
wp_die( __( 'Access denied.', 'groups' ) );
|
||||
}
|
||||
|
||||
if ( !wp_verify_nonce( $_POST[GROUPS_ADMIN_GROUPS_NONCE], 'groups-remove' ) ) {
|
||||
wp_die( __( 'Access denied.', 'groups' ) );
|
||||
}
|
||||
|
||||
$group_id = isset( $_POST['group-id-field'] ) ? $_POST['group-id-field'] : null;
|
||||
$group = Groups_Group::read( $group_id );
|
||||
if ( $group ) {
|
||||
if ( $group->name !== Groups_Registered::REGISTERED_GROUP_NAME ) {
|
||||
$result = Groups_Group::delete( $group_id );
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
} // function groups_admin_groups_remove_submit
|
||||
|
||||
/**
|
||||
* Shows form to confirm bulk-removal of groups.
|
||||
*/
|
||||
function groups_admin_groups_bulk_remove() {
|
||||
|
||||
global $wpdb;
|
||||
|
||||
$output = '';
|
||||
|
||||
if ( !current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
|
||||
wp_die( __( 'Access denied.', 'groups' ) );
|
||||
}
|
||||
|
||||
$group_ids = isset( $_POST['group_ids'] ) ? $_POST['group_ids'] : null;
|
||||
|
||||
if ( ! $group_ids ) {
|
||||
wp_die( __( 'No such groups.', 'groups' ) );
|
||||
}
|
||||
|
||||
$groups = array();
|
||||
foreach ( $group_ids as $group_id ) {
|
||||
$group = Groups_Group::read( intval( $group_id ) );
|
||||
if ( $group ) {
|
||||
$groups[] = $group;
|
||||
}
|
||||
}
|
||||
|
||||
$group_table = _groups_get_tablename( 'group' );
|
||||
|
||||
$current_url = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
|
||||
$current_url = remove_query_arg( 'action', $current_url );
|
||||
$current_url = remove_query_arg( 'group_id', $current_url );
|
||||
|
||||
$output .= '<div class="manage-groups wrap">';
|
||||
$output .= '<h1>';
|
||||
$output .= __( 'Remove groups', 'groups' );
|
||||
$output .= '</h1>';
|
||||
|
||||
$output .= '<form id="groups-action" method="post" action="">';
|
||||
$output .= '<div class="group remove">';
|
||||
|
||||
$output .= '<p>';
|
||||
$output .= __( 'Please confirm removal of the following groups. This action cannot be undone.', 'groups' );
|
||||
$output .= '</p>';
|
||||
|
||||
foreach ( $groups as $group ) {
|
||||
$output .= '<input id="group_ids" name="group_ids[]" type="hidden" value="' . esc_attr( intval( $group->group_id ) ) . '"/>';
|
||||
$output .= '<ul>';
|
||||
$output .= '<li>';
|
||||
$output .= sprintf( __( '<strong>%s</strong>', 'groups' ), wp_filter_nohtml_kses( $group->name ) );
|
||||
$output .= '</li>';
|
||||
$output .= '</ul>';
|
||||
}
|
||||
$output .= '<input class="button button-primary" type="submit" name="bulk" value="' . __( "Remove", 'groups' ) . '"/>';
|
||||
$output .= '<a class="cancel button" href="' . esc_url( $current_url ) . '">' . __( 'Cancel', 'groups' ) . '</a>';
|
||||
|
||||
$output .= '<input type="hidden" name="action" value="groups-action"/>';
|
||||
$output .= '<input type="hidden" name="bulk-action" value="remove-group"/>';
|
||||
$output .= '<input type="hidden" name="confirm" value="1"/>';
|
||||
$output .= wp_nonce_field( 'admin', GROUPS_ADMIN_GROUPS_ACTION_NONCE, true, false );
|
||||
|
||||
$output .= '</div>';
|
||||
$output .= '</form>';
|
||||
$output .= '</div>';
|
||||
|
||||
echo $output;
|
||||
} // function groups_admin_groups_bulk_remove
|
||||
|
||||
/**
|
||||
* Handle remove form submission.
|
||||
* @return array of deleted groups' ids
|
||||
*/
|
||||
function groups_admin_groups_bulk_remove_submit() {
|
||||
global $wpdb;
|
||||
|
||||
$result = array();
|
||||
if ( !current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
|
||||
wp_die( __( 'Access denied.', 'groups' ) );
|
||||
}
|
||||
|
||||
if ( !wp_verify_nonce( $_POST[GROUPS_ADMIN_GROUPS_ACTION_NONCE], 'admin' ) ) {
|
||||
wp_die( __( 'Access denied.', 'groups' ) );
|
||||
}
|
||||
|
||||
$group_ids = isset( $_POST['group_ids'] ) ? $_POST['group_ids'] : null;
|
||||
if ( $group_ids ) {
|
||||
foreach ( $group_ids as $group_id ) {
|
||||
$group = Groups_Group::read( $group_id );
|
||||
if ( $group ) {
|
||||
if ( $group->name !== Groups_Registered::REGISTERED_GROUP_NAME ) {
|
||||
if ( Groups_Group::delete( $group_id ) ) {
|
||||
$result[] = $group->group_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
} // function groups_admin_groups_bulk_remove_submit
|
||||
@@ -0,0 +1,545 @@
|
||||
<?php
|
||||
/**
|
||||
* groups-admin-groups.php
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.0.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// admin defines
|
||||
define( 'GROUPS_GROUPS_PER_PAGE', 10 );
|
||||
define( 'GROUPS_ADMIN_GROUPS_NONCE_1', 'groups-nonce-1');
|
||||
define( 'GROUPS_ADMIN_GROUPS_NONCE_2', 'groups-nonce-2');
|
||||
define( 'GROUPS_ADMIN_GROUPS_ACTION_NONCE', 'groups-action-nonce');
|
||||
define( 'GROUPS_ADMIN_GROUPS_FILTER_NONCE', 'groups-filter-nonce' );
|
||||
|
||||
require_once( GROUPS_CORE_LIB . '/class-groups-pagination.php' );
|
||||
require_once( GROUPS_ADMIN_LIB . '/groups-admin-groups-add.php');
|
||||
require_once( GROUPS_ADMIN_LIB . '/groups-admin-groups-edit.php');
|
||||
require_once( GROUPS_ADMIN_LIB . '/groups-admin-groups-remove.php');
|
||||
|
||||
/**
|
||||
* Manage Groups: table of groups and add, edit, remove actions.
|
||||
*/
|
||||
function groups_admin_groups() {
|
||||
|
||||
global $wpdb;
|
||||
|
||||
$output = '';
|
||||
$today = date( 'Y-m-d', time() );
|
||||
|
||||
if ( !current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
|
||||
wp_die( __( 'Access denied.', 'groups' ) );
|
||||
}
|
||||
|
||||
//
|
||||
// handle actions
|
||||
//
|
||||
if ( isset( $_POST['action'] ) ) {
|
||||
// handle action submit - do it
|
||||
switch( $_POST['action'] ) {
|
||||
case 'add' :
|
||||
if ( !( $group_id = groups_admin_groups_add_submit() ) ) {
|
||||
return groups_admin_groups_add();
|
||||
} else {
|
||||
$group = Groups_Group::read( $group_id );
|
||||
Groups_Admin::add_message( sprintf( __( "The <em>%s</em> group has been created.", 'groups' ), stripslashes( wp_filter_nohtml_kses( $group->name ) ) ) );
|
||||
}
|
||||
break;
|
||||
case 'edit' :
|
||||
if ( !( $group_id = groups_admin_groups_edit_submit() ) ) {
|
||||
return groups_admin_groups_edit( $_POST['group-id-field'] );
|
||||
} else {
|
||||
$group = Groups_Group::read( $group_id );
|
||||
Groups_Admin::add_message( sprintf( __( 'The <em>%s</em> group has been updated.', 'groups' ), stripslashes( wp_filter_nohtml_kses( $group->name ) ) ) );
|
||||
}
|
||||
break;
|
||||
case 'remove' :
|
||||
if ( $group_id = groups_admin_groups_remove_submit() ) {
|
||||
Groups_Admin::add_message( __( 'The group has been deleted.', 'groups' ) );
|
||||
}
|
||||
break;
|
||||
// bulk actions on groups: add capabilities, remove capabilities, remove groups
|
||||
case 'groups-action' :
|
||||
if ( wp_verify_nonce( $_POST[GROUPS_ADMIN_GROUPS_ACTION_NONCE], 'admin' ) ) {
|
||||
$group_ids = isset( $_POST['group_ids'] ) ? $_POST['group_ids'] : null;
|
||||
$bulk_action = null;
|
||||
if ( isset( $_POST['bulk'] ) ) {
|
||||
$bulk_action = $_POST['bulk-action'];
|
||||
}
|
||||
if ( is_array( $group_ids ) && ( $bulk_action !== null ) ) {
|
||||
foreach ( $group_ids as $group_id ) {
|
||||
switch ( $bulk_action ) {
|
||||
case 'add-capability' :
|
||||
$capabilities_id = isset( $_POST['capability_id'] ) ? $_POST['capability_id'] : null;
|
||||
if ( $capabilities_id !== null ) {
|
||||
foreach ( $capabilities_id as $capability_id ) {
|
||||
Groups_Group_Capability::create( array( 'group_id' => $group_id, 'capability_id' => $capability_id ) );
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'remove-capability' :
|
||||
$capabilities_id = isset( $_POST['capability_id'] ) ? $_POST['capability_id'] : null;
|
||||
if ( $capabilities_id !== null ) {
|
||||
foreach ( $capabilities_id as $capability_id ) {
|
||||
Groups_Group_Capability::delete( $group_id, $capability_id );
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'remove-group' :
|
||||
$bulk_confirm = isset( $_POST['confirm'] ) ? true : false;
|
||||
if ( $bulk_confirm ) {
|
||||
groups_admin_groups_bulk_remove_submit();
|
||||
} else {
|
||||
return groups_admin_groups_bulk_remove();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else if ( isset ( $_GET['action'] ) ) {
|
||||
// handle action request - show form
|
||||
switch( $_GET['action'] ) {
|
||||
case 'add' :
|
||||
return groups_admin_groups_add();
|
||||
break;
|
||||
case 'edit' :
|
||||
if ( isset( $_GET['group_id'] ) ) {
|
||||
return groups_admin_groups_edit( $_GET['group_id'] );
|
||||
}
|
||||
break;
|
||||
case 'remove' :
|
||||
if ( isset( $_GET['group_id'] ) ) {
|
||||
return groups_admin_groups_remove( $_GET['group_id'] );
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// group table
|
||||
//
|
||||
if (
|
||||
isset( $_POST['clear_filters'] ) ||
|
||||
isset( $_POST['group_id'] ) ||
|
||||
isset( $_POST['group_name'] )
|
||||
) {
|
||||
if ( !wp_verify_nonce( $_POST[GROUPS_ADMIN_GROUPS_FILTER_NONCE], 'admin' ) ) {
|
||||
wp_die( __( 'Access denied.', 'groups' ) );
|
||||
}
|
||||
}
|
||||
|
||||
// filters
|
||||
$group_id = Groups_Options::get_user_option( 'groups_group_id', null );
|
||||
$group_name = Groups_Options::get_user_option( 'groups_group_name', null );
|
||||
|
||||
if ( isset( $_POST['clear_filters'] ) ) {
|
||||
Groups_Options::delete_user_option( 'groups_group_id' );
|
||||
Groups_Options::delete_user_option( 'groups_group_name' );
|
||||
$group_id = null;
|
||||
$group_name = null;
|
||||
} else if ( isset( $_POST['submitted'] ) ) {
|
||||
// filter by name
|
||||
if ( !empty( $_POST['group_name'] ) ) {
|
||||
$group_name = $_POST['group_name'];
|
||||
Groups_Options::update_user_option( 'groups_group_name', $group_name );
|
||||
}
|
||||
// filter by group id
|
||||
if ( !empty( $_POST['group_id'] ) ) {
|
||||
$group_id = intval( $_POST['group_id'] );
|
||||
Groups_Options::update_user_option( 'groups_group_id', $group_id );
|
||||
} else if ( isset( $_POST['group_id'] ) ) { // empty && isset => '' => all
|
||||
$group_id = null;
|
||||
Groups_Options::delete_user_option( 'groups_group_id' );
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $_POST['row_count'] ) ) {
|
||||
if ( !wp_verify_nonce( $_POST[GROUPS_ADMIN_GROUPS_NONCE_1], 'admin' ) ) {
|
||||
wp_die( __( 'Access denied.', 'groups' ) );
|
||||
}
|
||||
}
|
||||
|
||||
if ( isset( $_POST['paged'] ) ) {
|
||||
if ( !wp_verify_nonce( $_POST[GROUPS_ADMIN_GROUPS_NONCE_2], 'admin' ) ) {
|
||||
wp_die( __( 'Access denied.', 'groups' ) );
|
||||
}
|
||||
}
|
||||
|
||||
$current_url = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
|
||||
$current_url = remove_query_arg( 'paged', $current_url );
|
||||
$current_url = remove_query_arg( 'action', $current_url );
|
||||
$current_url = remove_query_arg( 'group_id', $current_url );
|
||||
|
||||
$group_table = _groups_get_tablename( 'group' );
|
||||
|
||||
$output .=
|
||||
'<div class="manage-groups wrap">' .
|
||||
'<h1>' .
|
||||
_x( 'Groups', 'page-title', 'groups' ) .
|
||||
sprintf(
|
||||
'<a title="%s" class="add page-title-action" href="%s">',
|
||||
esc_attr( __( 'Click to add a new group', 'groups' ) ),
|
||||
esc_url( $current_url . '&action=add' )
|
||||
) .
|
||||
sprintf(
|
||||
'<img class="icon" alt="%s" src="%s" />',
|
||||
esc_attr( __( 'Add', 'groups' ) ),
|
||||
esc_url( GROUPS_PLUGIN_URL . 'images/add.png' )
|
||||
) .
|
||||
sprintf(
|
||||
'<span class="label">%s</span>',
|
||||
stripslashes( wp_filter_nohtml_kses( __( 'New Group', 'groups' ) ) )
|
||||
) .
|
||||
'</a>' .
|
||||
'</h1>';
|
||||
|
||||
$output .= Groups_Admin::render_messages();
|
||||
|
||||
$row_count = isset( $_POST['row_count'] ) ? intval( $_POST['row_count'] ) : 0;
|
||||
|
||||
if ($row_count <= 0) {
|
||||
$row_count = Groups_Options::get_user_option( 'groups_per_page', GROUPS_GROUPS_PER_PAGE );
|
||||
} else {
|
||||
Groups_Options::update_user_option('groups_per_page', $row_count );
|
||||
}
|
||||
$offset = isset( $_GET['offset'] ) ? intval( $_GET['offset'] ) : 0;
|
||||
if ( $offset < 0 ) {
|
||||
$offset = 0;
|
||||
}
|
||||
$paged = isset( $_REQUEST['paged'] ) ? intval( $_REQUEST['paged'] ) : 0;
|
||||
if ( $paged < 0 ) {
|
||||
$paged = 0;
|
||||
}
|
||||
|
||||
$orderby = isset( $_GET['orderby'] ) ? $_GET['orderby'] : null;
|
||||
switch ( $orderby ) {
|
||||
case 'group_id' :
|
||||
case 'name' :
|
||||
case 'description' :
|
||||
break;
|
||||
default:
|
||||
$orderby = 'name';
|
||||
}
|
||||
|
||||
$order = isset( $_GET['order'] ) ? $_GET['order'] : null;
|
||||
switch ( $order ) {
|
||||
case 'asc' :
|
||||
case 'ASC' :
|
||||
$switch_order = 'DESC';
|
||||
break;
|
||||
case 'desc' :
|
||||
case 'DESC' :
|
||||
$switch_order = 'ASC';
|
||||
break;
|
||||
default:
|
||||
$order = 'ASC';
|
||||
$switch_order = 'DESC';
|
||||
}
|
||||
|
||||
$filters = array( " 1=%d " );
|
||||
$filter_params = array( 1 );
|
||||
if ( $group_id ) {
|
||||
$filters[] = " $group_table.group_id = %d ";
|
||||
$filter_params[] = $group_id;
|
||||
}
|
||||
if ( $group_name ) {
|
||||
$filters[] = " $group_table.name LIKE '%%%s%%' ";
|
||||
$filter_params[] = $group_name;
|
||||
}
|
||||
|
||||
if ( !empty( $filters ) ) {
|
||||
$filters = " WHERE " . implode( " AND ", $filters );
|
||||
} else {
|
||||
$filters = '';
|
||||
}
|
||||
|
||||
$count_query = $wpdb->prepare( "SELECT COUNT(*) FROM $group_table $filters", $filter_params );
|
||||
$count = $wpdb->get_var( $count_query );
|
||||
if ( $count > $row_count ) {
|
||||
$paginate = true;
|
||||
} else {
|
||||
$paginate = false;
|
||||
}
|
||||
$pages = ceil ( $count / $row_count );
|
||||
if ( $paged > $pages ) {
|
||||
$paged = $pages;
|
||||
}
|
||||
if ( $paged != 0 ) {
|
||||
$offset = ( $paged - 1 ) * $row_count;
|
||||
}
|
||||
|
||||
$query = $wpdb->prepare(
|
||||
"SELECT * FROM $group_table
|
||||
$filters
|
||||
ORDER BY $orderby $order
|
||||
LIMIT $row_count OFFSET $offset",
|
||||
$filter_params
|
||||
);
|
||||
|
||||
$results = $wpdb->get_results( $query, OBJECT );
|
||||
|
||||
$column_display_names = array(
|
||||
'group_id' => __( 'ID', 'groups' ),
|
||||
'name' => __( 'Group', 'groups' ),
|
||||
'description' => __( 'Description', 'groups' ),
|
||||
'capabilities' => __( 'Capabilities', 'groups' )
|
||||
);
|
||||
|
||||
$output .= '<div class="groups-overview">';
|
||||
|
||||
$output .=
|
||||
'<div class="filters">' .
|
||||
'<form id="setfilters" action="" method="post">' .
|
||||
'<fieldset>' .
|
||||
'<legend>' . __( 'Filters', 'groups' ) . '</legend>' .
|
||||
'<label class="group-id-filter">' . __( 'Group ID', 'groups' ) . ' ' .
|
||||
'<input class="group-id-filter" name="group_id" type="text" value="' . esc_attr( $group_id ) . '"/>' .
|
||||
'</label>' . ' ' .
|
||||
'<label class="group-name-filter">' . __( 'Group Name', 'groups' ) . ' ' .
|
||||
'<input class="group-name-filter" name="group_name" type="text" value="' . $group_name . '"/>' .
|
||||
'</label>' . ' ' .
|
||||
wp_nonce_field( 'admin', GROUPS_ADMIN_GROUPS_FILTER_NONCE, true, false ) .
|
||||
'<input class="button" type="submit" value="' . __( 'Apply', 'groups' ) . '"/>' . ' ' .
|
||||
'<input class="button" type="submit" name="clear_filters" value="' . __( 'Clear', 'groups' ) . '"/>' .
|
||||
'<input type="hidden" value="submitted" name="submitted"/>' .
|
||||
'</fieldset>' .
|
||||
'</form>' .
|
||||
'</div>';
|
||||
|
||||
if ( $paginate ) {
|
||||
require_once( GROUPS_CORE_LIB . '/class-groups-pagination.php' );
|
||||
$pagination = new Groups_Pagination( $count, null, $row_count );
|
||||
$output .= '<form id="posts-filter" method="post" action="">';
|
||||
$output .= '<div>';
|
||||
$output .= wp_nonce_field( 'admin', GROUPS_ADMIN_GROUPS_NONCE_2, true, false );
|
||||
$output .= '</div>';
|
||||
$output .= '<div class="tablenav top">';
|
||||
$output .= $pagination->pagination( 'top' );
|
||||
$output .= '</div>';
|
||||
$output .= '</form>';
|
||||
}
|
||||
|
||||
$output .= '<div class="page-options right">';
|
||||
$output .= '<form id="setrowcount" action="" method="post">';
|
||||
$output .= '<div>';
|
||||
$output .= '<label for="row_count">' . __('Results per page', 'groups' ) . '</label>';
|
||||
$output .= '<input name="row_count" type="text" size="2" value="' . esc_attr( $row_count ) .'" />';
|
||||
$output .= wp_nonce_field( 'admin', GROUPS_ADMIN_GROUPS_NONCE_1, true, false );
|
||||
$output .= '<input class="button" type="submit" value="' . __( 'Apply', 'groups' ) . '"/>';
|
||||
$output .= '</div>';
|
||||
$output .= '</form>';
|
||||
$output .= '</div>';
|
||||
|
||||
$capability_table = _groups_get_tablename( "capability" );
|
||||
$group_capability_table = _groups_get_tablename( "group_capability" );
|
||||
|
||||
// capabilities select
|
||||
$capabilities = $wpdb->get_results( "SELECT * FROM $capability_table ORDER BY capability" );
|
||||
$capabilities_select = sprintf(
|
||||
'<select class="select capability" name="capability_id[]" multiple="multiple" placeholder="%s" data-placeholder="%s">',
|
||||
esc_attr( __( 'Capabilities …', 'groups' ) ) ,
|
||||
esc_attr( __( 'Capabilities …', 'groups' ) )
|
||||
);
|
||||
foreach( $capabilities as $capability ) {
|
||||
$capabilities_select .= sprintf( '<option value="%s">%s</option>', esc_attr( $capability->capability_id ), wp_filter_nohtml_kses( $capability->capability ) );
|
||||
}
|
||||
$capabilities_select .= '</select>';
|
||||
$capabilities_select .= Groups_UIE::render_select( '.select.capability' );
|
||||
|
||||
$output .= '<form id="groups-action" method="post" action="">';
|
||||
|
||||
$output .= '<div class="tablenav top">';
|
||||
|
||||
$output .= '<div class="groups-bulk-container">';
|
||||
$output .= '<div class="capabilities-select-container">';
|
||||
$output .= $capabilities_select;
|
||||
$output .= wp_nonce_field( 'admin', GROUPS_ADMIN_GROUPS_ACTION_NONCE, true, false );
|
||||
$output .= '</div>';
|
||||
$output .= '<select class="bulk-action" name="bulk-action">';
|
||||
$output .= '<option selected="selected" value="-1">' . esc_html( __( 'Bulk Actions', 'groups' ) ) . '</option>';
|
||||
$output .= '<option value="remove-group">' . esc_html( __( 'Remove group', 'groups' ) ) . '</option>';
|
||||
$output .= '<option value="add-capability">' . esc_html( __( 'Add capability', 'groups' ) ) . '</option>';
|
||||
$output .= '<option value="remove-capability">' . esc_html( __( 'Remove capability', 'groups' ) ) . '</option>';
|
||||
$output .= '</select>';
|
||||
$output .= sprintf( '<input class="button" type="submit" name="bulk" value="%s" />', esc_attr( __( 'Apply', 'groups' ) ) );
|
||||
$output .= '<input type="hidden" name="action" value="groups-action"/>';
|
||||
$output .= '</div>';
|
||||
$output .= '</div>';
|
||||
|
||||
$output .= '<table id="" class="wp-list-table widefat fixed" cellspacing="0">';
|
||||
$output .= '<thead>';
|
||||
$output .= '<tr>';
|
||||
|
||||
$output .= '<th id="cb" class="manage-column column-cb check-column" scope="col"><input type="checkbox"></th>';
|
||||
|
||||
foreach ( $column_display_names as $key => $column_display_name ) {
|
||||
$options = array(
|
||||
'orderby' => $key,
|
||||
'order' => $switch_order
|
||||
);
|
||||
$class = $key;
|
||||
if ( !in_array( $key, array( 'capabilities' ) ) ) {
|
||||
if ( strcmp( $key, $orderby ) == 0 ) {
|
||||
$lorder = strtolower( $order );
|
||||
$class = "$key manage-column sorted $lorder";
|
||||
} else {
|
||||
$class = "$key manage-column sortable";
|
||||
}
|
||||
$column_display_name =
|
||||
sprintf(
|
||||
'<a href="%s"><span>%s</span><span class="sorting-indicator"></span></a>',
|
||||
esc_url( add_query_arg( $options, $current_url ) ),
|
||||
esc_html( $column_display_name )
|
||||
);
|
||||
} else {
|
||||
$column_display_name = esc_html( $column_display_name );
|
||||
}
|
||||
$output .= sprintf(
|
||||
'<th scope="col" class="%s">%s</th>',
|
||||
esc_attr( $class ),
|
||||
$column_display_name
|
||||
);
|
||||
}
|
||||
|
||||
$output .= '</tr>';
|
||||
$output .= '</thead>';
|
||||
$output .= '<tbody>';
|
||||
|
||||
if ( count( $results ) > 0 ) {
|
||||
for ( $i = 0; $i < count( $results ); $i++ ) {
|
||||
|
||||
$result = $results[$i];
|
||||
|
||||
// Construct the "edit" URL.
|
||||
$edit_url = add_query_arg(
|
||||
array(
|
||||
'group_id' => intval( $result->group_id ),
|
||||
'action' => 'edit',
|
||||
'paged' => $paged
|
||||
),
|
||||
$current_url
|
||||
);
|
||||
|
||||
// Construct the "delete" URL.
|
||||
$delete_url = add_query_arg(
|
||||
array(
|
||||
'group_id' => intval( $result->group_id ),
|
||||
'action' => 'remove',
|
||||
'paged' => $paged
|
||||
),
|
||||
$current_url
|
||||
);
|
||||
|
||||
// Construct row actions for this group.
|
||||
$row_actions =
|
||||
'<div class="row-actions">' .
|
||||
'<span class="edit">' .
|
||||
'<a href="' . esc_url( $edit_url ) . '">' .
|
||||
'<img src="' . GROUPS_PLUGIN_URL . 'images/edit.png"/>' .
|
||||
__( 'Edit', 'groups' ) .
|
||||
'</a>';
|
||||
if ( $result->name !== Groups_Registered::REGISTERED_GROUP_NAME ) {
|
||||
$row_actions .=
|
||||
' | ' .
|
||||
'</span>' .
|
||||
'<span class="remove trash">' .
|
||||
'<a href="' . esc_url( $delete_url ) . '" class="submitdelete">' .
|
||||
'<img src="' . GROUPS_PLUGIN_URL . 'images/remove.png"/>' .
|
||||
__( 'Remove', 'groups' ) .
|
||||
'</a>' .
|
||||
'</span>';
|
||||
}
|
||||
$row_actions .= '</div>'; // .row-actions
|
||||
|
||||
$output .= '<tr class="' . ( $i % 2 == 0 ? 'even' : 'odd' ) . '">';
|
||||
|
||||
$output .= '<th class="check-column">';
|
||||
$output .= '<input type="checkbox" value="' . esc_attr( $result->group_id ) . '" name="group_ids[]"/>';
|
||||
$output .= '</th>';
|
||||
|
||||
$output .= '<td class="group-id">';
|
||||
$output .= $result->group_id;
|
||||
$output .= '</td>';
|
||||
$output .= '<td class="group-name">';
|
||||
$output .= sprintf( '<a href="%s">%s</a>', esc_url( $edit_url ), stripslashes( wp_filter_nohtml_kses( $result->name ) ) );
|
||||
$output .= $row_actions;
|
||||
$output .= '</td>';
|
||||
$output .= '<td class="group-description">';
|
||||
$output .= stripslashes( wp_filter_nohtml_kses( $result->description ) );
|
||||
$output .= '</td>';
|
||||
|
||||
$output .= '<td class="capabilities">';
|
||||
|
||||
$group = new Groups_Group( $result->group_id );
|
||||
$group_capabilities = $group->capabilities;
|
||||
$group_capabilities_deep = $group->capabilities_deep;
|
||||
usort( $group_capabilities_deep, array( 'Groups_Utility', 'cmp' ) );
|
||||
|
||||
if ( count( $group_capabilities_deep ) > 0 ) {
|
||||
$output .= '<ul>';
|
||||
foreach ( $group_capabilities_deep as $group_capability ) {
|
||||
$output .= '<li>';
|
||||
$class = '';
|
||||
if ( empty( $group_capabilities ) || !in_array( $group_capability, $group_capabilities ) ) {
|
||||
$class = 'inherited';
|
||||
}
|
||||
$output .= sprintf( '<span class="%s">', $class );
|
||||
if ( isset( $group_capability->capability ) && isset( $group_capability->capability->capability ) ) {
|
||||
$output .= wp_filter_nohtml_kses( $group_capability->capability->capability );
|
||||
}
|
||||
$output .= '</span>';
|
||||
$output .= '</li>';
|
||||
}
|
||||
$output .= '</ul>';
|
||||
} else {
|
||||
$output .= __( 'This group has no capabilities.', 'groups' );
|
||||
}
|
||||
$output .= '</td>';
|
||||
|
||||
$output .= '</tr>';
|
||||
}
|
||||
} else {
|
||||
$output .= '<tr><td colspan="4">' . __( 'There are no results.', 'groups' ) . '</td></tr>';
|
||||
}
|
||||
|
||||
$output .= '</tbody>';
|
||||
$output .= '</table>';
|
||||
|
||||
$output .= Groups_UIE::render_add_titles( '.groups-overview table td' );
|
||||
|
||||
$output .= '</form>'; // #groups-action
|
||||
|
||||
if ( $paginate ) {
|
||||
require_once( GROUPS_CORE_LIB . '/class-groups-pagination.php' );
|
||||
$pagination = new Groups_Pagination($count, null, $row_count);
|
||||
$output .= '<div class="tablenav bottom">';
|
||||
$output .= $pagination->pagination( 'bottom' );
|
||||
$output .= '</div>';
|
||||
}
|
||||
|
||||
$output .= '</div>'; // .groups-overview
|
||||
$output .= '</div>'; // .manage-groups
|
||||
|
||||
echo $output;
|
||||
} // function groups_admin_groups()
|
||||
@@ -0,0 +1,403 @@
|
||||
<?php
|
||||
/**
|
||||
* groups-admin-options.php
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.0.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @var string options form nonce name
|
||||
*/
|
||||
define( 'GROUPS_ADMIN_OPTIONS_NONCE', 'groups-admin-nonce' );
|
||||
|
||||
/**
|
||||
* Options admin screen.
|
||||
*/
|
||||
function groups_admin_options() {
|
||||
|
||||
global $wpdb, $wp_roles;
|
||||
|
||||
if ( !current_user_can( GROUPS_ADMINISTER_OPTIONS ) ) {
|
||||
wp_die( __( 'Access denied.', 'groups' ) );
|
||||
}
|
||||
|
||||
$is_sitewide_plugin = false;
|
||||
if ( is_multisite() ) {
|
||||
$active_sitewide_plugins = get_site_option( 'active_sitewide_plugins', array() );
|
||||
$active_sitewide_plugins = array_keys( $active_sitewide_plugins );
|
||||
$is_sitewide_plugin = in_array( 'groups/groups.php', $active_sitewide_plugins );
|
||||
}
|
||||
|
||||
$caps = array(
|
||||
GROUPS_ACCESS_GROUPS => __( 'Access Groups', 'groups' ),
|
||||
GROUPS_ADMINISTER_GROUPS => __( 'Administer Groups', 'groups' ),
|
||||
GROUPS_ADMINISTER_OPTIONS => __( 'Administer Groups plugin options', 'groups' ),
|
||||
GROUPS_RESTRICT_ACCESS => __( 'Restrict Access', 'groups' )
|
||||
);
|
||||
|
||||
$previous_legacy_enable = Groups_Options::get_option( GROUPS_LEGACY_ENABLE, GROUPS_LEGACY_ENABLE_DEFAULT );
|
||||
|
||||
//
|
||||
// handle options form submission
|
||||
//
|
||||
if ( isset( $_POST['submit'] ) ) {
|
||||
if ( wp_verify_nonce( $_POST[GROUPS_ADMIN_OPTIONS_NONCE], 'admin' ) ) {
|
||||
|
||||
$post_types = get_post_types();
|
||||
$selected_post_types = !empty( $_POST['add_meta_boxes'] ) && is_array( $_POST['add_meta_boxes'] ) ? $_POST['add_meta_boxes'] : array();
|
||||
foreach( $post_types as $post_type ) {
|
||||
$handle_post_types[$post_type] = in_array( $post_type, $selected_post_types );
|
||||
}
|
||||
Groups_Post_Access::set_handles_post_types( $handle_post_types );
|
||||
|
||||
// tree view
|
||||
if ( !empty( $_POST[GROUPS_SHOW_TREE_VIEW] ) ) {
|
||||
Groups_Options::update_option( GROUPS_SHOW_TREE_VIEW, true );
|
||||
} else {
|
||||
Groups_Options::update_option( GROUPS_SHOW_TREE_VIEW, false );
|
||||
}
|
||||
|
||||
// show in user profiles
|
||||
Groups_Options::update_option( GROUPS_SHOW_IN_USER_PROFILE, !empty( $_POST[GROUPS_SHOW_IN_USER_PROFILE] ) );
|
||||
|
||||
// roles & capabilities
|
||||
$rolenames = $wp_roles->get_names();
|
||||
foreach ( $rolenames as $rolekey => $rolename ) {
|
||||
$role = $wp_roles->get_role( $rolekey );
|
||||
foreach ( $caps as $capkey => $capname ) {
|
||||
$role_cap_id = $rolekey.'-'.$capkey;
|
||||
if ( !empty($_POST[$role_cap_id] ) ) {
|
||||
$role->add_cap( $capkey );
|
||||
} else {
|
||||
$role->remove_cap( $capkey );
|
||||
}
|
||||
}
|
||||
}
|
||||
Groups_Controller::assure_capabilities();
|
||||
|
||||
if ( !$is_sitewide_plugin ) {
|
||||
// delete data
|
||||
if ( !empty( $_POST['delete-data'] ) ) {
|
||||
Groups_Options::update_option( 'groups_delete_data', true );
|
||||
} else {
|
||||
Groups_Options::update_option( 'groups_delete_data', false );
|
||||
}
|
||||
}
|
||||
|
||||
// legacy enable ?
|
||||
if ( !empty( $_POST[GROUPS_LEGACY_ENABLE] ) ) {
|
||||
Groups_Options::update_option( GROUPS_LEGACY_ENABLE, true );
|
||||
} else {
|
||||
Groups_Options::update_option( GROUPS_LEGACY_ENABLE, false );
|
||||
}
|
||||
|
||||
Groups_Admin::add_message( __( 'Options saved.', 'groups' ) );
|
||||
}
|
||||
}
|
||||
|
||||
echo '<div class="groups-options wrap">';
|
||||
|
||||
echo
|
||||
'<h1>' .
|
||||
__( 'Groups Options', 'groups' ) .
|
||||
'</h1>';
|
||||
|
||||
echo Groups_Admin::render_messages();
|
||||
|
||||
$show_tree_view = Groups_Options::get_option( GROUPS_SHOW_TREE_VIEW, GROUPS_SHOW_TREE_VIEW_DEFAULT );
|
||||
$show_in_user_profile = Groups_Options::get_option( GROUPS_SHOW_IN_USER_PROFILE, GROUPS_SHOW_IN_USER_PROFILE_DEFAULT );
|
||||
|
||||
$rolenames = $wp_roles->get_names();
|
||||
$caps_table = '<table class="groups-permissions">';
|
||||
$caps_table .= '<thead>';
|
||||
$caps_table .= '<tr>';
|
||||
$caps_table .= '<td class="role">';
|
||||
$caps_table .= __( 'Role', 'groups' );
|
||||
$caps_table .= '</td>';
|
||||
foreach ( $caps as $cap ) {
|
||||
$caps_table .= '<td class="cap">';
|
||||
$caps_table .= $cap;
|
||||
$caps_table .= '</td>';
|
||||
}
|
||||
|
||||
$caps_table .= '</tr>';
|
||||
$caps_table .= '</thead>';
|
||||
$caps_table .= '<tbody>';
|
||||
foreach ( $rolenames as $rolekey => $rolename ) {
|
||||
$role = $wp_roles->get_role( $rolekey );
|
||||
$caps_table .= '<tr>';
|
||||
$caps_table .= '<td>';
|
||||
$caps_table .= translate_user_role( $rolename );
|
||||
$caps_table .= '</td>';
|
||||
foreach ( $caps as $capkey => $capname ) {
|
||||
|
||||
if ( $role->has_cap( $capkey ) ) {
|
||||
$checked = ' checked="checked" ';
|
||||
} else {
|
||||
$checked = '';
|
||||
}
|
||||
|
||||
$caps_table .= '<td class="checkbox">';
|
||||
$role_cap_id = $rolekey.'-'.$capkey;
|
||||
$caps_table .= '<input type="checkbox" name="' . $role_cap_id . '" id="' . $role_cap_id . '" ' . $checked . '/>';
|
||||
$caps_table .= '</td>';
|
||||
}
|
||||
$caps_table .= '</tr>';
|
||||
}
|
||||
$caps_table .= '</tbody>';
|
||||
$caps_table .= '</table>';
|
||||
|
||||
$delete_data = Groups_Options::get_option( 'groups_delete_data', false );
|
||||
|
||||
if ( isset( $_GET['dismiss-groups-extensions-box'] ) && isset( $_GET['groups-extensions-box-nonce'] ) && wp_verify_nonce( $_GET['groups-extensions-box-nonce'], 'dismiss-box' ) ) {
|
||||
Groups_Options::update_user_option( 'show-extensions-box', false );
|
||||
}
|
||||
$extensions_box = '';
|
||||
if ( Groups_Options::get_user_option( 'show-extensions-box', true ) ) {
|
||||
$dismiss_url = wp_nonce_url( add_query_arg( 'dismiss-groups-extensions-box', '1', admin_url( 'admin.php?page=groups-admin-options' ) ), 'dismiss-box', 'groups-extensions-box-nonce' );
|
||||
$extensions_box =
|
||||
'<div id="groups-extensions-box">' .
|
||||
__( 'Enhanced functionality is available via official <a href="http://www.itthinx.com/shop/">Extensions</a> for Groups.', 'groups' ) .
|
||||
sprintf( '<a class="close" href="%s">x</a>', esc_url( $dismiss_url ) ) .
|
||||
'</div>';
|
||||
}
|
||||
|
||||
//
|
||||
// print the options form
|
||||
//
|
||||
echo
|
||||
'<form action="" name="options" method="post">' .
|
||||
'<div>' .
|
||||
|
||||
'<p>' .
|
||||
'<input class="button button-primary" type="submit" name="submit" value="' . __( 'Save', 'groups' ) . '"/>' .
|
||||
$extensions_box .
|
||||
'</p>';
|
||||
|
||||
if ( _groups_admin_override() ) {
|
||||
echo
|
||||
'<h2 style="color:red">' .
|
||||
__( 'Administrator Access Override', 'groups' ) .
|
||||
'</h2>' .
|
||||
'<p>' .
|
||||
__( 'Administrators override all access permissions derived from Groups capabilities.', 'groups' ) .
|
||||
'</p>' .
|
||||
'<p>' .
|
||||
__( 'To disable, do not define the constant <code>GROUPS_ADMINISTRATOR_OVERRIDE</code> or set it to <code>false</code>.', 'groups' ) .
|
||||
'</p>' .
|
||||
'<p>' .
|
||||
__( 'Enabling this on production sites is <strong>not</strong> recommended.', 'groups' ) .
|
||||
'</p>';
|
||||
}
|
||||
|
||||
echo '<h2>';
|
||||
echo __( 'Access restricions', 'groups' );
|
||||
echo '</h2>';
|
||||
|
||||
echo '<h3>';
|
||||
echo __( 'Post types', 'groups' );
|
||||
echo '</h3>';
|
||||
|
||||
echo '<p class="description">';
|
||||
echo __( 'Show access restrictions for these post types.', 'groups' ); // @todo change wording to '...handles access...' ?
|
||||
echo '</p>';
|
||||
|
||||
$post_type_objects = get_post_types( array(), 'objects' );
|
||||
uasort( $post_type_objects, 'groups_admin_options_compare_post_types' );
|
||||
|
||||
echo '<ul>';
|
||||
foreach( $post_type_objects as $post_type => $post_type_object ) {
|
||||
echo '<li>';
|
||||
echo '<label>';
|
||||
$label = $post_type;
|
||||
$labels = isset( $post_type_object->labels ) ? $post_type_object->labels : null;
|
||||
if ( ( $labels !== null ) && isset( $labels->singular_name ) ) {
|
||||
$label = __( $labels->singular_name );
|
||||
}
|
||||
$checked = Groups_Post_Access::handles_post_type( $post_type ) ? ' checked="checked" ' : '';
|
||||
echo '<input name="add_meta_boxes[]" type="checkbox" value="' . esc_attr( $post_type ) . '" ' . $checked . '/>';
|
||||
$is_public = isset( $post_type_object->public ) && $post_type_object->public;
|
||||
echo $is_public ? '<strong>' : '';
|
||||
echo esc_html( $label );
|
||||
echo $is_public ? '</strong>' : '';
|
||||
if ( $post_type != $label ) {
|
||||
echo ' ';
|
||||
echo '<code><small>';
|
||||
echo esc_html( $post_type );
|
||||
echo '</small></code>';
|
||||
}
|
||||
echo '</label>';
|
||||
echo '</li>';
|
||||
}
|
||||
echo '<ul>';
|
||||
echo
|
||||
'<p class="description">' .
|
||||
__( 'This determines for which post types access restriction settings are offered.', 'groups' ) . '<br/>' .
|
||||
__( 'Disabling this setting for a post type also disables existing access restrictions on individual posts of that type.', 'groups' ) . '<br/>' .
|
||||
'</p>';
|
||||
|
||||
echo
|
||||
'<h2>' . __( 'User profiles', 'groups' ) . '</h2>' .
|
||||
'<p>' .
|
||||
'<label>' .
|
||||
'<input name="' . GROUPS_SHOW_IN_USER_PROFILE . '" type="checkbox" ' . ( $show_in_user_profile ? 'checked="checked"' : '' ) . '/>' .
|
||||
__( 'Show groups in user profiles.', 'groups' ) .
|
||||
'</label>' .
|
||||
'</p>';
|
||||
|
||||
echo
|
||||
'<h2>' . __( 'Tree view', 'groups' ) . '</h2>' .
|
||||
'<p>' .
|
||||
'<label>' .
|
||||
'<input name="' . GROUPS_SHOW_TREE_VIEW . '" type="checkbox" ' . ( $show_tree_view ? 'checked="checked"' : '' ) . '/>' .
|
||||
__( 'Show the Groups tree view.', 'groups' ) .
|
||||
'</label>' .
|
||||
'</p>';
|
||||
|
||||
echo
|
||||
'<h2>' . __( 'Permissions', 'groups' ) . '</h2>' .
|
||||
'<p>' . __( 'These permissions apply to Groups management. They do not apply to access permissions derived from Groups capabilities.', 'groups' ) . '</p>' .
|
||||
$caps_table .
|
||||
'<p class="description">' .
|
||||
__( 'A minimum set of permissions will be preserved.', 'groups' ) .
|
||||
'<br/>' .
|
||||
__( 'If you lock yourself out, please ask an administrator to help.', 'groups' ) .
|
||||
'</p>';
|
||||
if ( !$is_sitewide_plugin ) {
|
||||
echo
|
||||
'<h2>' . __( 'Deactivation and data persistence', 'groups' ) . '</h2>' .
|
||||
'<p>' .
|
||||
'<label>' .
|
||||
'<input name="delete-data" type="checkbox" ' . ( $delete_data ? 'checked="checked"' : '' ) . '/>' .
|
||||
__( 'Delete all Groups plugin data on deactivation', 'groups' ) .
|
||||
'</label>' .
|
||||
'</p>' .
|
||||
'<p class="description warning">' .
|
||||
__( 'CAUTION: If this option is active while the plugin is deactivated, ALL plugin settings and data will be DELETED. If you are going to use this option, now would be a good time to make a backup. By enabling this option you agree to be solely responsible for any loss of data or any other consequences thereof.', 'groups' ) .
|
||||
'</p>';
|
||||
}
|
||||
|
||||
$groups_legacy_enable = Groups_Options::get_option( GROUPS_LEGACY_ENABLE, GROUPS_LEGACY_ENABLE_DEFAULT );
|
||||
echo '<h2>' . __( 'Legacy Settings', 'groups' ) . '</h2>';
|
||||
echo '<p>' .
|
||||
'<label>' .
|
||||
'<input name="' . GROUPS_LEGACY_ENABLE . '" type="checkbox" ' . ( $groups_legacy_enable ? 'checked="checked"' : '' ) . '/>' .
|
||||
__( 'Enable legacy access control based on capabilities.', 'groups' ) .
|
||||
'</label>' .
|
||||
'</p>';
|
||||
if ( $groups_legacy_enable ) {
|
||||
require_once GROUPS_LEGACY_LIB . '/admin/groups-admin-options-legacy.php';
|
||||
do_action( 'groups_admin_options_legacy', $groups_legacy_enable !== $previous_legacy_enable );
|
||||
}
|
||||
|
||||
echo
|
||||
'<p>' .
|
||||
wp_nonce_field( 'admin', GROUPS_ADMIN_OPTIONS_NONCE, true, false ) .
|
||||
'<input class="button button-primary" type="submit" name="submit" value="' . __( 'Save', 'groups' ) . '"/>' .
|
||||
'</p>' .
|
||||
'</div>' .
|
||||
'</form>';
|
||||
|
||||
echo '</div>'; // .groups-options
|
||||
}
|
||||
|
||||
/**
|
||||
* Network administration options.
|
||||
*/
|
||||
function groups_network_admin_options() {
|
||||
|
||||
if ( !current_user_can( GROUPS_ADMINISTER_OPTIONS ) ) {
|
||||
wp_die( __( 'Access denied.', 'groups' ) );
|
||||
}
|
||||
|
||||
echo
|
||||
'<div>' .
|
||||
'<h1>' .
|
||||
__( 'Groups network options', 'groups' ) .
|
||||
'</h1>' .
|
||||
'</div>';
|
||||
|
||||
// handle options form submission
|
||||
if ( isset( $_POST['submit'] ) ) {
|
||||
if ( wp_verify_nonce( $_POST[GROUPS_ADMIN_OPTIONS_NONCE], 'admin' ) ) {
|
||||
// delete data
|
||||
if ( !empty( $_POST['delete-data'] ) ) {
|
||||
Groups_Options::update_option( 'groups_network_delete_data', true );
|
||||
} else {
|
||||
Groups_Options::update_option( 'groups_network_delete_data', false );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$delete_data = Groups_Options::get_option( 'groups_network_delete_data', false );
|
||||
|
||||
// options form
|
||||
echo
|
||||
'<form action="" name="options" method="post">' .
|
||||
'<div>' .
|
||||
'<h2>' . __( 'Network deactivation and data persistence', 'groups' ) . '</h2>' .
|
||||
'<p>' .
|
||||
'<label>' .
|
||||
'<input name="delete-data" type="checkbox" ' . ( $delete_data ? 'checked="checked"' : '' ) . '/>' .
|
||||
' ' .
|
||||
__( 'Delete all Groups plugin data for ALL sites on network deactivation', 'groups' ) .
|
||||
'</label>' .
|
||||
'</p>' .
|
||||
'<p class="description warning">' .
|
||||
__( 'CAUTION: If this option is active while the plugin is deactivated, ALL plugin settings and data will be DELETED for <strong>all sites</strong>. If you are going to use this option, now would be a good time to make a backup. By enabling this option you agree to be solely responsible for any loss of data or any other consequences thereof.', 'groups' ) .
|
||||
'</p>' .
|
||||
'<p>' .
|
||||
wp_nonce_field( 'admin', GROUPS_ADMIN_OPTIONS_NONCE, true, false ) .
|
||||
'<input class="button button-primary" type="submit" name="submit" value="' . __( 'Save', 'groups' ) . '"/>' .
|
||||
'</p>' .
|
||||
'</div>' .
|
||||
'</form>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare two post types, considering those that have $public and/or $show_ui true as coming first.
|
||||
* @param object $o1
|
||||
* @param object $o2
|
||||
* @return int
|
||||
*/
|
||||
function groups_admin_options_compare_post_types( $o1, $o2 ) {
|
||||
$name_1 = isset( $o1->name ) ? $o1->name : '';
|
||||
$name_2 = isset( $o2->name ) ? $o2->name : '';
|
||||
$public_1 = isset( $o1->public ) && $o1->public;
|
||||
$public_2 = isset( $o2->public ) && $o2->public;
|
||||
$show_ui_1 = isset( $o1->show_ui ) && $o1->show_ui;
|
||||
$show_ui_2 = isset( $o2->show_ui ) && $o2->show_ui;
|
||||
$n1 = 0;
|
||||
$n2 = 0;
|
||||
if ( $public_1 ) {
|
||||
$n1--;
|
||||
}
|
||||
if ( $show_ui_1 ) {
|
||||
$n1--;
|
||||
}
|
||||
if ( $public_2 ) {
|
||||
$n2--;
|
||||
}
|
||||
if ( $show_ui_2 ) {
|
||||
$n2--;
|
||||
}
|
||||
return ( $n1 - $n2 ) * 10 + strcmp( $name_1, $name_2 );
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/**
|
||||
* groups-admin-tree-view.php
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.0.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tree view : a simple tree view
|
||||
*/
|
||||
function groups_admin_tree_view() {
|
||||
|
||||
global $wpdb;
|
||||
|
||||
$output = '';
|
||||
$today = date( 'Y-m-d', time() );
|
||||
|
||||
if ( !current_user_can( GROUPS_ACCESS_GROUPS ) ) {
|
||||
wp_die( __( 'Access denied.', 'groups' ) );
|
||||
}
|
||||
|
||||
$output .=
|
||||
'<div class="groups-tree-view">' .
|
||||
'<h1>' .
|
||||
__( 'Tree of Groups', 'groups' ) .
|
||||
'</h1>';
|
||||
|
||||
$tree = Groups_Utility::get_group_tree();
|
||||
$tree_output = '';
|
||||
Groups_Utility::render_group_tree( $tree, $tree_output );
|
||||
$output .= $tree_output;
|
||||
|
||||
$output .= '</div>'; // .groups-tree-view
|
||||
|
||||
echo $output;
|
||||
} // function groups_admin_tree_view()
|
||||
@@ -0,0 +1,202 @@
|
||||
<?php
|
||||
/**
|
||||
* class-groups-registered.php
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.0.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* "Registered" group automation.
|
||||
*/
|
||||
class Groups_Registered {
|
||||
|
||||
const REGISTERED_GROUP_NAME = 'Registered';
|
||||
|
||||
const BATCH_LIMIT = 100;
|
||||
|
||||
/**
|
||||
* Creates groups for registered users.
|
||||
* Must be called explicitly or hooked into activation.
|
||||
*
|
||||
* As of Groups 2.2.0 this does not trigger the 'groups_created_user_group' action for each entry.
|
||||
*/
|
||||
public static function activate() {
|
||||
|
||||
global $wpdb;
|
||||
|
||||
// create a group for the blog if it doesn't exist
|
||||
if ( !( $group = Groups_Group::read_by_name( self::REGISTERED_GROUP_NAME ) ) ) {
|
||||
$group_id = Groups_Group::create( array( 'name' => self::REGISTERED_GROUP_NAME ) );
|
||||
} else {
|
||||
$group_id = $group->group_id;
|
||||
}
|
||||
if ( $group_id ) {
|
||||
$user_group_table = _groups_get_tablename( 'user_group' );
|
||||
$query = $wpdb->prepare(
|
||||
"INSERT IGNORE INTO $user_group_table " .
|
||||
"SELECT ID, %d FROM $wpdb->users",
|
||||
Groups_Utility::id( $group_id )
|
||||
);
|
||||
$rows = $wpdb->query( $query );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize hooks that handle addition and removal of users and blogs.
|
||||
*/
|
||||
public static function init() {
|
||||
|
||||
// For translation of the "Registered" group(s)
|
||||
__( 'Registered', 'groups' );
|
||||
|
||||
// When a blog is added, create a new "Registered" group for that blog.
|
||||
add_action( 'wpmu_new_blog', array( __CLASS__, 'wpmu_new_blog' ), 10, 2 );
|
||||
|
||||
// Remove group when a blog is deleted? When a blog is deleted,
|
||||
// Groups_Controller::delete_blog() takes appropriate action.
|
||||
|
||||
// When a user is added, add it to the "Registered" group.
|
||||
add_action( 'user_register', array( __CLASS__, 'user_register' ) );
|
||||
|
||||
// Note : When a user is deleted this is handled from core.
|
||||
|
||||
// When a user is added to a blog, add it to the blog's "Registered" group.
|
||||
add_action( 'add_user_to_blog', array( __CLASS__, 'add_user_to_blog' ), 10, 3 );
|
||||
|
||||
// Note : When a user is removed from a blog it's handled from core.
|
||||
}
|
||||
|
||||
/**
|
||||
* Create "Registered" group for new blog and add its admin user.
|
||||
*
|
||||
* @see Groups_Controller::wpmu_new_blog()
|
||||
*
|
||||
* @param int $blog_id
|
||||
* @param int $user_id blog's admin user's id
|
||||
* @param string $domain (optional)
|
||||
* @param string $path (optional)
|
||||
* @param int $site_id (optional)
|
||||
* @param array $meta (optional)
|
||||
*/
|
||||
public static function wpmu_new_blog( $blog_id, $user_id, $domain = null, $path = null, $site_id = null, $meta = null ) {
|
||||
if ( is_multisite() ) {
|
||||
Groups_Controller::switch_to_blog( $blog_id );
|
||||
}
|
||||
if ( !( $group = Groups_Group::read_by_name( self::REGISTERED_GROUP_NAME ) ) ) {
|
||||
$group_id = Groups_Group::create( array( 'name' => self::REGISTERED_GROUP_NAME ) );
|
||||
} else {
|
||||
$group_id = $group->group_id;
|
||||
}
|
||||
// add the blog's admin user to the group
|
||||
if ( $group_id ) {
|
||||
if ( !Groups_User_Group::read( $user_id, $group_id ) ) {
|
||||
Groups_User_Group::create( array( 'user_id' => $user_id, 'group_id' => $group_id ) );
|
||||
}
|
||||
}
|
||||
if ( is_multisite() ) {
|
||||
Groups_Controller::restore_current_blog();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign a newly created user to its "Registered" group.
|
||||
*
|
||||
* @param int $user_id
|
||||
*/
|
||||
public static function user_register( $user_id ) {
|
||||
|
||||
$registered_group = Groups_Group::read_by_name( self::REGISTERED_GROUP_NAME );
|
||||
if ( !$registered_group ) {
|
||||
$registered_group_id = Groups_Group::create( array( 'name' => self::REGISTERED_GROUP_NAME ) );
|
||||
} else {
|
||||
$registered_group_id = $registered_group->group_id;
|
||||
}
|
||||
if ( $registered_group_id ) {
|
||||
// Multisite: If a new user registers with the main blog,
|
||||
// the user is added, but it doesn't appear on the Users admin
|
||||
// screen of the main blog. It doesn't have the Subscriber role
|
||||
// (or any other) for that blog, unless it is explicitly added by the
|
||||
// blog's admin to the site. In other words, a user that has just
|
||||
// registered with the site's main blog can access the profile page
|
||||
// on the back end, but doesn't appear as a user to the site's admin.
|
||||
// Currently, on WP 3.3.2, like it or not, it's like that.
|
||||
// Unless the user actually has a capability (role)
|
||||
// for a blog, it won't appear in the blog's users list. After
|
||||
// registering with the blog, the user does not have a capability.
|
||||
// Thus, we need to check that is_user_member_of_blog( $user_id ) here.
|
||||
if ( !is_multisite() || is_user_member_of_blog( $user_id ) ) {
|
||||
Groups_User_Group::create(
|
||||
array(
|
||||
'user_id' => $user_id,
|
||||
'group_id' => $registered_group_id
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assign a user to its "Registered" group for the given blog.
|
||||
*
|
||||
* @param int $user_id User ID.
|
||||
* @param string $role User role.
|
||||
* @param int $blog_id Blog ID.
|
||||
*/
|
||||
public static function add_user_to_blog( $user_id, $role, $blog_id ) {
|
||||
|
||||
if ( is_multisite() ) {
|
||||
Groups_Controller::switch_to_blog( $blog_id );
|
||||
}
|
||||
|
||||
global $wpdb;
|
||||
|
||||
// Check if the group table exists, if it does not exist, we are
|
||||
// probably here because the action has been triggered in the middle
|
||||
// of wpmu_create_blog() before the wpmu_new_blog action has been
|
||||
// triggered. In that case, just skip this as the user will be added
|
||||
// later when wpmu_new_blog is triggered, the activation sequence has
|
||||
// created the tables and all users of the new blog are added to
|
||||
// that blog's "Registered" group.
|
||||
$group_table = _groups_get_tablename( 'group' );
|
||||
if ( $wpdb->get_var( "SHOW TABLES LIKE '" . $group_table . "'" ) == $group_table ) {
|
||||
$registered_group = Groups_Group::read_by_name( self::REGISTERED_GROUP_NAME );
|
||||
if ( !$registered_group ) {
|
||||
$registered_group_id = Groups_Group::create( array( 'name' => self::REGISTERED_GROUP_NAME ) );
|
||||
} else {
|
||||
$registered_group_id = $registered_group->group_id;
|
||||
}
|
||||
if ( $registered_group_id ) {
|
||||
Groups_User_Group::create(
|
||||
array(
|
||||
'user_id' => $user_id,
|
||||
'group_id' => $registered_group_id
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if ( is_multisite() ) {
|
||||
Groups_Controller::restore_current_blog();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Groups_Registered::init();
|
||||
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
/**
|
||||
* class-groups-cache-object.php
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.9.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache entry encapsulation.
|
||||
*
|
||||
* @property string $key
|
||||
* @property mixed $value
|
||||
*/
|
||||
class Groups_Cache_Object {
|
||||
|
||||
/**
|
||||
* Cache key.
|
||||
* @var string
|
||||
*/
|
||||
private $key = null;
|
||||
|
||||
/**
|
||||
* Cached value.
|
||||
* @var mixed
|
||||
*/
|
||||
private $value = null;
|
||||
|
||||
/**
|
||||
* Create a cache entry object that holds a value for the given key.
|
||||
*
|
||||
* @param string $key
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function __construct( $key, $value ) {
|
||||
$this->key = $key;
|
||||
$this->value = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Getter implementation for key and value properties.
|
||||
*
|
||||
* @param string $name
|
||||
* @return property value or null
|
||||
*/
|
||||
public function __get( $name ) {
|
||||
$result = null;
|
||||
switch ( $name ) {
|
||||
case 'key' :
|
||||
case 'value' :
|
||||
$result = $this->$name;
|
||||
break;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Setter for key and value properties.
|
||||
*
|
||||
* @param string $name
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function __set( $name, $value ) {
|
||||
switch( $name ) {
|
||||
case 'key' :
|
||||
case 'value' :
|
||||
$this->$name = $value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
/**
|
||||
* class-groups-cache.php
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.9.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache service.
|
||||
*
|
||||
* Uses cache objects to encapsulate cached data.
|
||||
*
|
||||
* This makes us completely independent from the problems related to
|
||||
* incomplete cache implementations that ignore the $found parameter used
|
||||
* to disambiguate cache misses with wp_cache_get() when false is retrieved.
|
||||
*/
|
||||
class Groups_Cache {
|
||||
|
||||
/**
|
||||
* Default cache group.
|
||||
* @var string
|
||||
*/
|
||||
const CACHE_GROUP = 'groups';
|
||||
|
||||
/**
|
||||
* Retrieve an entry from cache.
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $group
|
||||
* @return Groups_Cache_Object|null returns a cache object on hit, null on cache miss
|
||||
*/
|
||||
public static function get( $key, $group = self::CACHE_GROUP ) {
|
||||
$found = null;
|
||||
$value = wp_cache_get( $key, $group, false, $found );
|
||||
if ( !( $value instanceof Groups_Cache_Object ) ) {
|
||||
$value = null;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store an entry in cache.
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $value
|
||||
* @param string $group
|
||||
* @return true if successful, otherwise false
|
||||
*/
|
||||
public static function set( $key, $value, $group = self::CACHE_GROUP ) {
|
||||
$object = new Groups_Cache_Object( $key, $value );
|
||||
return wp_cache_set( $key, $object, $group );
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a cache entry.
|
||||
*
|
||||
* @param string $key
|
||||
* @param string $group
|
||||
* @return true if successful, otherwise false
|
||||
*/
|
||||
public static function delete( $key, $group = self::CACHE_GROUP ) {
|
||||
return wp_cache_delete( $key, $group );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,313 @@
|
||||
<?php
|
||||
/**
|
||||
* class-groups-capability.php
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.0.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Capability OPM
|
||||
*/
|
||||
class Groups_Capability {
|
||||
|
||||
const CACHE_GROUP = 'groups';
|
||||
const READ_BY_CAPABILITY = 'read_by_capability';
|
||||
const READ_CAPABILITY_BY_ID = 'read_capability_by_id';
|
||||
|
||||
/**
|
||||
* @var persisted capability object
|
||||
*/
|
||||
var $capability = null;
|
||||
|
||||
/**
|
||||
* Create by capability id.
|
||||
* Must have been persisted.
|
||||
* @param int $capability_id
|
||||
*/
|
||||
public function __construct( $capability_id ) {
|
||||
$this->capability = self::read( $capability_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a property by name.
|
||||
*
|
||||
* Possible properties:
|
||||
* - capability_id
|
||||
* - capability
|
||||
* - class
|
||||
* - object
|
||||
* - name
|
||||
* - description
|
||||
*
|
||||
* - group_ids groups that have the capability
|
||||
*
|
||||
* @param string $name property's name
|
||||
* @return property value, will return null if property does not exist
|
||||
*/
|
||||
public function __get( $name ) {
|
||||
|
||||
global $wpdb;
|
||||
|
||||
$result = null;
|
||||
if ( $this->capability !== null ) {
|
||||
switch( $name ) {
|
||||
case 'capability_id' :
|
||||
case 'capability' :
|
||||
case 'class' :
|
||||
case 'object' :
|
||||
case 'name' :
|
||||
case 'description' :
|
||||
$result = $this->capability->$name;
|
||||
break;
|
||||
case 'group_ids' :
|
||||
$group_capability_table = _groups_get_tablename( 'group_capability' );
|
||||
$rows = $wpdb->get_results( $wpdb->prepare(
|
||||
"SELECT group_id FROM $group_capability_table WHERE capability_id = %d",
|
||||
Groups_Utility::id( $this->capability->capability_id )
|
||||
) );
|
||||
if ( $rows ) {
|
||||
$result = array();
|
||||
foreach( $rows as $row ) {
|
||||
$result[] = $row->group_id;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'groups' :
|
||||
$group_capability_table = _groups_get_tablename( 'group_capability' );
|
||||
$rows = $wpdb->get_results( $wpdb->prepare(
|
||||
"SELECT group_id FROM $group_capability_table WHERE capability_id = %d",
|
||||
Groups_Utility::id( $this->capability->capability_id )
|
||||
) );
|
||||
if ( $rows ) {
|
||||
$result = array();
|
||||
foreach( $rows as $row ) {
|
||||
$result[] = new Groups_Group( $row->group_id );
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist a capability.
|
||||
*
|
||||
* Possible keys in $map:
|
||||
*
|
||||
* - "capability" (required) - unique capability label, max 20 characters
|
||||
* - "class" (optional) - class the capability applies to, max 100 chars
|
||||
* - "object" (optional) - identifies object of that class, max 100 chars
|
||||
* - "name" (optional) - name it if you have to
|
||||
* - "description" (optional) - dito
|
||||
*
|
||||
* @param array $map attributes, requires at least: "capability"
|
||||
* @return capability_id on success, otherwise false
|
||||
*/
|
||||
public static function create( $map ) {
|
||||
|
||||
global $wpdb;
|
||||
extract( $map );
|
||||
$result = false;
|
||||
|
||||
if ( !empty( $capability ) ) {
|
||||
|
||||
if ( self::read_by_capability( $capability ) === false ) {
|
||||
|
||||
$data = array(
|
||||
'capability' => $capability
|
||||
);
|
||||
$formats = array( '%s' );
|
||||
|
||||
if ( !empty( $class ) ) {
|
||||
$data['class'] = $class;
|
||||
$formats[] = '%s';
|
||||
}
|
||||
if ( !empty( $object ) ) {
|
||||
$data['object'] = $object;
|
||||
$formats[] = '%s';
|
||||
}
|
||||
if ( !empty( $name ) ) {
|
||||
$data['name'] = $name;
|
||||
$formats[] = '%s';
|
||||
}
|
||||
if ( !empty( $description ) ) {
|
||||
$data['description'] = $description;
|
||||
$formats[] = '%s';
|
||||
}
|
||||
$capability_table = _groups_get_tablename( 'capability' );
|
||||
if ( $wpdb->insert( $capability_table, $data, $formats ) ) {
|
||||
if ( $result = $wpdb->get_var( "SELECT LAST_INSERT_ID()" ) ) {
|
||||
// read_by_capability above created a cache entry which needs to be reset
|
||||
Groups_Cache::delete( self::READ_BY_CAPABILITY . '_' . $capability, self::CACHE_GROUP );
|
||||
do_action( 'groups_created_capability', $result );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a capability.
|
||||
*
|
||||
* Use Groups_Capability::read_capability() if you are trying to retrieve a capability by its unique label.
|
||||
*
|
||||
* @see Groups_Capability::read_by_capability()
|
||||
* @param int $capability_id capability's id
|
||||
* @return object upon success, otherwise false
|
||||
*/
|
||||
public static function read( $capability_id ) {
|
||||
global $wpdb;
|
||||
$result = false;
|
||||
$cached = Groups_Cache::get( self::READ_CAPABILITY_BY_ID . '_' . $capability_id, self::CACHE_GROUP );
|
||||
if ( $cached !== null ) {
|
||||
$result = $cached->value;
|
||||
unset( $cached );
|
||||
} else {
|
||||
$capability_table = _groups_get_tablename( 'capability' );
|
||||
$capability = $wpdb->get_row( $wpdb->prepare(
|
||||
"SELECT * FROM $capability_table WHERE capability_id = %d",
|
||||
Groups_Utility::id( $capability_id )
|
||||
) );
|
||||
if ( isset( $capability->capability_id ) ) {
|
||||
$result = $capability;
|
||||
}
|
||||
Groups_Cache::set( self::READ_CAPABILITY_BY_ID . '_' . $capability_id, $result, self::CACHE_GROUP );
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a capability by its unique label.
|
||||
*
|
||||
* @param string $capability capability's unique label
|
||||
* @return object upon success, otherwise false
|
||||
*/
|
||||
public static function read_by_capability( $capability ) {
|
||||
global $wpdb;
|
||||
$_capability = $capability;
|
||||
$cached = Groups_Cache::get( self::READ_BY_CAPABILITY . '_' . $_capability, self::CACHE_GROUP );
|
||||
if ( $cached !== null ) {
|
||||
$result = $cached->value;
|
||||
unset( $cached );
|
||||
} else {
|
||||
$result = false;
|
||||
$capability_table = _groups_get_tablename( 'capability' );
|
||||
$capability = $wpdb->get_row( $wpdb->prepare(
|
||||
"SELECT * FROM $capability_table WHERE capability = %s",
|
||||
$capability
|
||||
) );
|
||||
if ( isset( $capability->capability_id ) ) {
|
||||
$result = $capability;
|
||||
}
|
||||
Groups_Cache::set( self::READ_BY_CAPABILITY . '_' . $_capability, $result, self::CACHE_GROUP );
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update capability.
|
||||
*
|
||||
* @param array $map capability attribute, must contain capability_id
|
||||
* @return capability_id on success, otherwise false
|
||||
*/
|
||||
public static function update( $map ) {
|
||||
|
||||
global $wpdb;
|
||||
extract( $map );
|
||||
$result = false;
|
||||
|
||||
if ( isset( $capability_id ) && !empty( $capability ) ) {
|
||||
$capability_table = _groups_get_tablename( 'capability' );
|
||||
$old_capability = Groups_Capability::read( $capability_id );
|
||||
if ( $old_capability ) {
|
||||
if ( isset( $capability ) ) {
|
||||
$old_capability_capability = $old_capability->capability;
|
||||
$old_capability->capability = $capability;
|
||||
}
|
||||
if ( isset( $class ) ) {
|
||||
$old_capability->class = $class;
|
||||
}
|
||||
if ( isset( $object ) ) {
|
||||
$old_capability->object = $object;
|
||||
}
|
||||
if ( isset( $name ) ) {
|
||||
$old_name = $old_capability->name;
|
||||
$old_capability->name = $name;
|
||||
}
|
||||
if ( isset( $description ) ) {
|
||||
$old_capability->description = $description;
|
||||
}
|
||||
$rows = $wpdb->query( $wpdb->prepare(
|
||||
"UPDATE $capability_table SET capability = %s, class = %s, object = %s, name = %s, description = %s WHERE capability_id = %d",
|
||||
$old_capability->capability,
|
||||
$old_capability->class,
|
||||
$old_capability->object,
|
||||
$old_capability->name,
|
||||
$old_capability->description,
|
||||
Groups_Utility::id( $capability_id )
|
||||
) );
|
||||
if ( ( $rows !== false ) ) {
|
||||
$result = $capability_id;
|
||||
if ( !empty( $old_capability ) && !empty( $old_capability->capability ) ) {
|
||||
Groups_Cache::delete( self::READ_BY_CAPABILITY . '_' . $old_capability->capability, self::CACHE_GROUP );
|
||||
}
|
||||
if ( !empty( $old_capability_capability ) ) {
|
||||
Groups_Cache::delete( self::READ_BY_CAPABILITY . '_' . $old_capability_capability, self::CACHE_GROUP );
|
||||
}
|
||||
do_action( 'groups_updated_capability', $result );
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove capability and its relations.
|
||||
*
|
||||
* @param int $capability_id
|
||||
* @return capability_id if successful, false otherwise
|
||||
*/
|
||||
public static function delete( $capability_id ) {
|
||||
|
||||
global $wpdb;
|
||||
$result = false;
|
||||
|
||||
// avoid nonsense requests
|
||||
if ( $capability = Groups_Capability::read( $capability_id ) ) {
|
||||
$capability_table = _groups_get_tablename( 'capability' );
|
||||
// get rid of it
|
||||
if ( $rows = $wpdb->query( $wpdb->prepare(
|
||||
"DELETE FROM $capability_table WHERE capability_id = %d",
|
||||
Groups_Utility::id( $capability_id )
|
||||
) ) ) {
|
||||
$result = $capability_id;
|
||||
if ( !empty( $capability->capability ) ) {
|
||||
Groups_Cache::delete( self::READ_BY_CAPABILITY . '_' . $capability->capability, self::CACHE_GROUP );
|
||||
do_action( 'groups_deleted_capability_capability', $capability->capability );
|
||||
}
|
||||
do_action( 'groups_deleted_capability', $result );
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,598 @@
|
||||
<?php
|
||||
/**
|
||||
* class-groups-controller.php
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.0.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugin controller
|
||||
*/
|
||||
class Groups_Controller {
|
||||
|
||||
/**
|
||||
* Version 2.0.0 number
|
||||
* @var string
|
||||
*/
|
||||
const GROUPS_200 = '2.0.0';
|
||||
|
||||
/**
|
||||
* Cache-safe switching in case any multi-site hiccups might occur.
|
||||
*
|
||||
* Clears the cache after switching to the given blog to avoid using
|
||||
* another blog's cached values.
|
||||
*
|
||||
* Some implementations don't have wp_cache_switch_to_blog() nor the deprecated
|
||||
* wp_cache_reset(), e.g. WP Engine's object-cache.php which has wp_cache_flush().
|
||||
*
|
||||
* See wp_cache_reset() in wp-includes/cache.php
|
||||
* @see wp_cache_switch_to_blog()
|
||||
* @see wp_cache_flush()
|
||||
* @see wp_cache_reset()
|
||||
* @link http://core.trac.wordpress.org/ticket/14941
|
||||
*
|
||||
* @param int $blog_id
|
||||
*/
|
||||
public static function switch_to_blog( $blog_id ) {
|
||||
switch_to_blog( $blog_id );
|
||||
if ( function_exists( 'wp_cache_switch_to_blog' ) ) {
|
||||
wp_cache_switch_to_blog( $blog_id ); // introduced in WP 3.5.0
|
||||
} else if ( function_exists( 'wp_cache_flush' ) ) {
|
||||
wp_cache_flush();
|
||||
} else if ( function_exists( 'wp_cache_reset' ) ) {
|
||||
wp_cache_reset(); // deprecated in WP 3.5.0
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch back to previous blog.
|
||||
*/
|
||||
public static function restore_current_blog() {
|
||||
restore_current_blog();
|
||||
}
|
||||
|
||||
/**
|
||||
* Boot the plugin.
|
||||
* @see Groups_Registered::wpmu_new_blog()
|
||||
*/
|
||||
public static function boot() {
|
||||
register_activation_hook( GROUPS_FILE, array( __CLASS__, 'activate' ) );
|
||||
register_deactivation_hook( GROUPS_FILE, array( __CLASS__, 'deactivate' ) );
|
||||
add_action( 'init', array( __CLASS__, 'init' ) );
|
||||
add_filter( 'load_textdomain_mofile', array( __CLASS__, 'load_textdomain_mofile' ), 10, 2 );
|
||||
// priority 9 because it needs to be called before Groups_Registered's
|
||||
// wpmu_new_blog kicks in
|
||||
add_action( 'wpmu_new_blog', array( __CLASS__, 'wpmu_new_blog' ), 9, 2 );
|
||||
add_action( 'delete_blog', array( __CLASS__, 'delete_blog' ), 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Run activation for a newly created blog in a multisite environment.
|
||||
*
|
||||
* @param int $blog_id
|
||||
*/
|
||||
public static function wpmu_new_blog( $blog_id, $user_id ) {
|
||||
if ( is_multisite() ) {
|
||||
$active_sitewide_plugins = get_site_option( 'active_sitewide_plugins', array() );
|
||||
if ( key_exists( 'groups/groups.php', $active_sitewide_plugins ) ) {
|
||||
self::switch_to_blog( $blog_id );
|
||||
self::setup();
|
||||
self::restore_current_blog();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Run deactivation for a blog that is about to be deleted in a multisite
|
||||
* environment.
|
||||
*
|
||||
* @param int $blog_id
|
||||
*/
|
||||
public static function delete_blog( $blog_id, $drop = false ) {
|
||||
if ( is_multisite() ) {
|
||||
$active_sitewide_plugins = get_site_option( 'active_sitewide_plugins', array() );
|
||||
if ( key_exists( 'groups/groups.php', $active_sitewide_plugins ) ) {
|
||||
self::switch_to_blog( $blog_id );
|
||||
self::cleanup( $drop );
|
||||
self::restore_current_blog();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize.
|
||||
* Loads the plugin's translations.
|
||||
* Invokes version check.
|
||||
*/
|
||||
public static function init() {
|
||||
|
||||
// Load our current translations first ...
|
||||
$mofile = self::get_mofile();
|
||||
load_textdomain( 'groups', $mofile );
|
||||
|
||||
// ... otherwise load_plugin_textdomain will simply get those in WP's languages
|
||||
// and we won't have our up-to-date translations.
|
||||
//load_plugin_textdomain( 'groups', null, 'groups/languages' );
|
||||
self::version_check();
|
||||
|
||||
// load the notice class
|
||||
if ( is_admin() ) {
|
||||
if ( current_user_can( 'activate_plugins' ) ) { // important: after init hook
|
||||
require_once GROUPS_ADMIN_LIB . '/class-groups-admin-notice.php';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the mofile string for our own translations.
|
||||
* @return string mofile
|
||||
*/
|
||||
private static function get_mofile() {
|
||||
$locale = apply_filters( 'plugin_locale', get_locale(), 'groups' );
|
||||
$mofile = GROUPS_CORE_DIR . '/languages/groups-' . $locale . '.mo';
|
||||
return $mofile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure that our own translation file is loaded first.
|
||||
*
|
||||
* @param string $mofile
|
||||
* @param string $domain
|
||||
* @return string mofile
|
||||
*/
|
||||
public static function load_textdomain_mofile( $mofile, $domain ) {
|
||||
$own_mofile = self::get_mofile();
|
||||
if ( $domain == 'groups' ) {
|
||||
if ( $own_mofile != $mofile ) {
|
||||
if ( !is_textdomain_loaded( $domain ) ) {
|
||||
if ( is_readable( $own_mofile ) ) {
|
||||
$mofile = $own_mofile;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $mofile;
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugin activation.
|
||||
* @param boolean $network_wide
|
||||
*/
|
||||
public static function activate( $network_wide = false ) {
|
||||
$sem_id = self::sem_get( self::get_sem_key() );
|
||||
if ( ( $sem_id === false ) || self::sem_acquire( $sem_id ) ) {
|
||||
if ( is_multisite() && $network_wide ) {
|
||||
$blog_ids = Groups_Utility::get_blogs();
|
||||
foreach ( $blog_ids as $blog_id ) {
|
||||
self::switch_to_blog( $blog_id );
|
||||
self::setup();
|
||||
self::restore_current_blog();
|
||||
}
|
||||
} else {
|
||||
self::setup();
|
||||
set_transient( 'groups_plugin_activated', true, 60 );
|
||||
}
|
||||
if ( $sem_id !== false ) {
|
||||
self::sem_release( $sem_id );
|
||||
self::sem_remove( $sem_id );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugin activation work.
|
||||
*/
|
||||
private static function setup() {
|
||||
global $wpdb, $wp_roles;
|
||||
|
||||
// create WP capabilities
|
||||
Groups_Controller::set_default_capabilities();
|
||||
|
||||
$charset_collate = '';
|
||||
if ( ! empty( $wpdb->charset ) ) {
|
||||
$charset_collate = "DEFAULT CHARACTER SET $wpdb->charset";
|
||||
}
|
||||
if ( ! empty( $wpdb->collate ) ) {
|
||||
$charset_collate .= " COLLATE $wpdb->collate";
|
||||
}
|
||||
|
||||
// create tables
|
||||
$group_table = _groups_get_tablename( 'group' );
|
||||
if ( $wpdb->get_var( "SHOW TABLES LIKE '$group_table'" ) != $group_table ) {
|
||||
$queries[] = "CREATE TABLE IF NOT EXISTS $group_table (
|
||||
group_id BIGINT(20) UNSIGNED NOT NULL auto_increment,
|
||||
parent_id BIGINT(20) DEFAULT NULL,
|
||||
creator_id BIGINT(20) DEFAULT NULL,
|
||||
datetime DATETIME DEFAULT NULL,
|
||||
name VARCHAR(100) NOT NULL,
|
||||
description LONGTEXT DEFAULT NULL,
|
||||
PRIMARY KEY (group_id),
|
||||
UNIQUE INDEX group_n (name)
|
||||
) $charset_collate;";
|
||||
}
|
||||
$capability_table = _groups_get_tablename( 'capability' );
|
||||
if ( $wpdb->get_var( "SHOW TABLES LIKE '$capability_table'" ) != $capability_table ) {
|
||||
$queries[] = "CREATE TABLE IF NOT EXISTS $capability_table (
|
||||
capability_id BIGINT(20) UNSIGNED NOT NULL auto_increment,
|
||||
capability VARCHAR(255) NOT NULL,
|
||||
class VARCHAR(255) DEFAULT NULL,
|
||||
object VARCHAR(255) DEFAULT NULL,
|
||||
name VARCHAR(100) DEFAULT NULL,
|
||||
description LONGTEXT DEFAULT NULL,
|
||||
PRIMARY KEY (capability_id),
|
||||
UNIQUE INDEX capability (capability(100)),
|
||||
INDEX capability_kco (capability(20),class(20),object(20))
|
||||
) $charset_collate;";
|
||||
}
|
||||
$user_group_table = _groups_get_tablename( 'user_group' );
|
||||
if ( $wpdb->get_var( "SHOW TABLES LIKE '$user_group_table'" ) != $user_group_table ) {
|
||||
$queries[] = "CREATE TABLE IF NOT EXISTS $user_group_table (
|
||||
user_id bigint(20) unsigned NOT NULL,
|
||||
group_id bigint(20) unsigned NOT NULL,
|
||||
PRIMARY KEY (user_id, group_id),
|
||||
INDEX user_group_gu (group_id,user_id)
|
||||
) $charset_collate;";
|
||||
}
|
||||
$user_capability_table = _groups_get_tablename( 'user_capability' );
|
||||
if ( $wpdb->get_var( "SHOW TABLES LIKE '$user_capability_table'" ) != $user_capability_table ) {
|
||||
$queries[] = "CREATE TABLE IF NOT EXISTS $user_capability_table (
|
||||
user_id bigint(20) unsigned NOT NULL,
|
||||
capability_id bigint(20) unsigned NOT NULL,
|
||||
PRIMARY KEY (user_id, capability_id),
|
||||
INDEX user_capability_cu (capability_id,user_id)
|
||||
) $charset_collate;";
|
||||
}
|
||||
$group_capability_table = _groups_get_tablename( 'group_capability' );
|
||||
if ( $wpdb->get_var( "SHOW TABLES LIKE '$group_capability_table'" ) != $group_capability_table ) {
|
||||
$queries[] = "CREATE TABLE IF NOT EXISTS $group_capability_table (
|
||||
group_id bigint(20) unsigned NOT NULL,
|
||||
capability_id bigint(20) unsigned NOT NULL,
|
||||
PRIMARY KEY (group_id, capability_id),
|
||||
INDEX group_capability_cg (capability_id,group_id)
|
||||
) $charset_collate;";
|
||||
}
|
||||
if ( !empty( $queries ) ) {
|
||||
// For the record ... (and https://core.trac.wordpress.org/ticket/12773 should not be closed)
|
||||
// dbDelta() fails to handle queries "CREATE TABLE IF NOT EXISTS ..."
|
||||
// (a regex results in "IF" used as array index holding only last query to create table).
|
||||
//require_once( ABSPATH . 'wp-admin/includes/upgrade.php' );
|
||||
//dbDelta( $queries );
|
||||
foreach( $queries as $query ) {
|
||||
$wpdb->query( $query );
|
||||
}
|
||||
}
|
||||
// needs to be called to create its capabilities
|
||||
Groups_Post_Access::activate();
|
||||
// same thing to created groups for registered users
|
||||
Groups_Registered::activate();
|
||||
// add WordPress capabilities
|
||||
Groups_WordPress::activate();
|
||||
// ... end of plugin activation work.
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks current version and triggers update if needed.
|
||||
*/
|
||||
public static function version_check() {
|
||||
global $groups_version, $groups_admin_messages;
|
||||
$previous_version = get_option( 'groups_plugin_version', null );
|
||||
$groups_version = GROUPS_CORE_VERSION;
|
||||
// auto-enable legacy support on upgrade from Groups previous to 2.0.0
|
||||
if ( $previous_version ) {
|
||||
if ( version_compare( $previous_version, self::GROUPS_200 ) < 0 ) {
|
||||
if ( Groups_Options::get_option( GROUPS_LEGACY_ENABLE ) === null ) {
|
||||
Groups_Options::update_option( GROUPS_LEGACY_ENABLE, true );
|
||||
}
|
||||
set_transient( 'groups_plugin_updated_legacy', true, 60 );
|
||||
}
|
||||
}
|
||||
|
||||
// disable legacy support on new installations
|
||||
if ( Groups_Options::get_option( GROUPS_LEGACY_ENABLE ) === null ) {
|
||||
Groups_Options::update_option( GROUPS_LEGACY_ENABLE, false );
|
||||
}
|
||||
|
||||
// run update procedure if newer version is installed
|
||||
if ( version_compare( $previous_version, $groups_version ) < 0 ) {
|
||||
if ( self::update( $previous_version ) ) {
|
||||
if ( update_option( 'groups_plugin_version', $groups_version ) ) {
|
||||
set_transient( 'groups_plugin_updated', true, 60 );
|
||||
}
|
||||
} else {
|
||||
$groups_admin_messages[] = '<div class="error">Updating Groups plugin core <em>failed</em>.</div>';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update maintenance.
|
||||
*/
|
||||
public static function update( $previous_version ) {
|
||||
|
||||
global $wpdb, $groups_admin_messages;
|
||||
|
||||
$result = true;
|
||||
|
||||
$sem_id = self::sem_get( self::get_sem_key() );
|
||||
if ( ( $sem_id === false ) || self::sem_acquire( $sem_id ) ) {
|
||||
$queries = array();
|
||||
switch ( $previous_version ) {
|
||||
case '1.0.0' :
|
||||
$capability_table = _groups_get_tablename( 'capability' );
|
||||
if ( $wpdb->get_var( "SHOW TABLES LIKE '$capability_table'" ) == $capability_table ) {
|
||||
// increase column sizes
|
||||
$queries[] = "ALTER TABLE $capability_table MODIFY capability VARCHAR(255) UNIQUE NOT NULL;";
|
||||
$queries[] = "ALTER TABLE $capability_table MODIFY class VARCHAR(255) DEFAULT NULL;";
|
||||
$queries[] = "ALTER TABLE $capability_table MODIFY object VARCHAR(255) DEFAULT NULL;";
|
||||
// correct capabilities
|
||||
$queries[] = "UPDATE $capability_table SET capability='delete_published_pages' WHERE capability='delete_published_pag';";
|
||||
$queries[] = "UPDATE $capability_table SET capability='delete_published_posts' WHERE capability='delete_published_pos';";
|
||||
// fix hideously big index
|
||||
$queries[] = "ALTER TABLE $capability_table DROP INDEX capability_kco;";
|
||||
$queries[] = "ALTER TABLE $capability_table ADD INDEX capability_kco (capability(20),class(20),object(20));";
|
||||
}
|
||||
break;
|
||||
case '1.0.0-beta-3d' :
|
||||
$capability_table = _groups_get_tablename( 'capability' );
|
||||
if ( $wpdb->get_var( "SHOW TABLES LIKE '$capability_table'" ) == $capability_table ) {
|
||||
// increase column sizes
|
||||
$queries[] = "ALTER TABLE $capability_table MODIFY capability VARCHAR(255) UNIQUE NOT NULL;";
|
||||
$queries[] = "ALTER TABLE $capability_table MODIFY class VARCHAR(255) DEFAULT NULL;";
|
||||
$queries[] = "ALTER TABLE $capability_table MODIFY object VARCHAR(255) DEFAULT NULL;";
|
||||
// correct capabilities
|
||||
$queries[] = "UPDATE $capability_table SET capability='delete_published_pages' WHERE capability='delete_published_pag';";
|
||||
$queries[] = "UPDATE $capability_table SET capability='delete_published_posts' WHERE capability='delete_published_pos';";
|
||||
}
|
||||
break;
|
||||
default :
|
||||
if ( !empty( $previous_version ) ) {
|
||||
if ( version_compare( $previous_version, '1.1.6' ) < 0 ) {
|
||||
Groups_Options::update_option( Groups_Post_Access::READ_POST_CAPABILITIES, array( Groups_Post_Access::READ_POST_CAPABILITY ) );
|
||||
$wpdb->query( $wpdb->prepare( "UPDATE $wpdb->postmeta SET meta_value = %s WHERE meta_key = %s", Groups_Post_Access::READ_POST_CAPABILITY, Groups_Post_Access::POSTMETA_PREFIX . Groups_Post_Access::READ_POST_CAPABILITY ) );
|
||||
}
|
||||
if ( version_compare( $previous_version, '1.5.1' ) < 0 ) {
|
||||
$capability_table = _groups_get_tablename( 'capability' );
|
||||
$queries[] = "ALTER TABLE $capability_table DROP INDEX capability, ADD UNIQUE INDEX capability(capability(100));";
|
||||
}
|
||||
}
|
||||
} // switch
|
||||
if ( !empty( $previous_version ) && version_compare( $previous_version, '2.0.0' ) < 0 ) {
|
||||
self::set_default_capabilities();
|
||||
Groups_WordPress::refresh_capabilities();
|
||||
}
|
||||
foreach ( $queries as $query ) {
|
||||
if ( $wpdb->query( $query ) === false ) {
|
||||
$result = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop tables and clear data if the plugin is deactivated.
|
||||
* This will happen only if the user chooses to delete data upon deactivation.
|
||||
* @param boolean $network_wide
|
||||
*/
|
||||
public static function deactivate( $network_wide = false ) {
|
||||
$sem_id = self::sem_get( self::get_sem_key() );
|
||||
if ( ( $sem_id === false ) || self::sem_acquire( $sem_id ) ) {
|
||||
if ( is_multisite() && $network_wide ) {
|
||||
if ( Groups_Options::get_option( 'groups_network_delete_data', false ) ) {
|
||||
$blog_ids = Groups_Utility::get_blogs();
|
||||
foreach ( $blog_ids as $blog_id ) {
|
||||
self::switch_to_blog( $blog_id );
|
||||
self::cleanup( true );
|
||||
self::restore_current_blog();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
self::cleanup();
|
||||
}
|
||||
if ( $sem_id !== false ) {
|
||||
self::sem_release( $sem_id );
|
||||
self::sem_remove( $sem_id );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Plugin deactivation cleanup.
|
||||
* @param $drop overrides the groups_delete_data option, default is false
|
||||
*/
|
||||
private static function cleanup( $drop = false ) {
|
||||
|
||||
global $wpdb, $wp_roles;
|
||||
|
||||
$delete_data = Groups_Options::get_option( 'groups_delete_data', false );
|
||||
if ( $delete_data || $drop ) {
|
||||
foreach ( $wp_roles->role_objects as $role ) {
|
||||
$role->remove_cap( GROUPS_ACCESS_GROUPS );
|
||||
$role->remove_cap( GROUPS_ADMINISTER_GROUPS );
|
||||
$role->remove_cap( GROUPS_ADMINISTER_OPTIONS );
|
||||
$role->remove_cap( GROUPS_RESTRICT_ACCESS );
|
||||
}
|
||||
$wpdb->query( 'DROP TABLE IF EXISTS ' . _groups_get_tablename( 'group' ) );
|
||||
$wpdb->query( 'DROP TABLE IF EXISTS ' . _groups_get_tablename( 'capability' ) );
|
||||
$wpdb->query( 'DROP TABLE IF EXISTS ' . _groups_get_tablename( 'user_group' ) );
|
||||
$wpdb->query( 'DROP TABLE IF EXISTS ' . _groups_get_tablename( 'user_capability' ) );
|
||||
$wpdb->query( 'DROP TABLE IF EXISTS ' . _groups_get_tablename( 'group_capability' ) );
|
||||
Groups_Options::flush_options();
|
||||
if ( class_exists( 'Groups_Admin_Notice' ) ) {
|
||||
delete_metadata( 'user', null, Groups_Admin_Notice::HIDE_REVIEW_NOTICE, null, true );
|
||||
delete_site_option( Groups_Admin_Notice::INIT_TIME );
|
||||
}
|
||||
delete_option( GROUPS_ADMINISTRATOR_ACCESS_OVERRIDE ); // keep this to delete the deprecated option @since 2.1.1
|
||||
delete_option( 'groups_plugin_version' );
|
||||
delete_option( 'groups_delete_data' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the default capabilities for the administrator role.
|
||||
* In lack of an administrator role, these capabilities are assigned
|
||||
* to any role that can manage_options.
|
||||
* This is also used to assure a minimum set of capabilities is
|
||||
* assigned to an appropriate role, so that it's not possible
|
||||
* to lock yourself out (although deactivating and then activating
|
||||
* the plugin would have the same effect but with the danger of
|
||||
* deleting all plugin data).
|
||||
* @param boolean $activate defaults to true, when this function is called upon plugin activation
|
||||
* @access private
|
||||
*/
|
||||
public static function set_default_capabilities() {
|
||||
global $wp_roles;
|
||||
// The administrator role should be there, if it's not, assign privileges to
|
||||
// any role that can manage_options:
|
||||
if ( $administrator_role = $wp_roles->get_role( 'administrator' ) ) {
|
||||
$administrator_role->add_cap( GROUPS_ACCESS_GROUPS );
|
||||
$administrator_role->add_cap( GROUPS_ADMINISTER_GROUPS );
|
||||
$administrator_role->add_cap( GROUPS_ADMINISTER_OPTIONS );
|
||||
$administrator_role->add_cap( GROUPS_RESTRICT_ACCESS );
|
||||
} else {
|
||||
foreach ( $wp_roles->role_objects as $role ) {
|
||||
if ($role->has_cap( 'manage_options' ) ) {
|
||||
$role->add_cap( GROUPS_ACCESS_GROUPS );
|
||||
$role->add_cap( GROUPS_ADMINISTER_GROUPS );
|
||||
$role->add_cap( GROUPS_ADMINISTER_OPTIONS );
|
||||
$role->add_cap( GROUPS_RESTRICT_ACCESS );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* There must be at least one role with the minimum set of capabilities
|
||||
* to access and manage the Groups plugin's options.
|
||||
* If this condition is not met, the minimum set of capabilities is
|
||||
* reestablished.
|
||||
*/
|
||||
public static function assure_capabilities() {
|
||||
global $wp_roles;
|
||||
$complies = false;
|
||||
$roles = $wp_roles->role_objects;
|
||||
foreach( $roles as $role ) {
|
||||
if ( $role->has_cap( GROUPS_ACCESS_GROUPS ) && ( $role->has_cap( GROUPS_ADMINISTER_OPTIONS ) ) ) {
|
||||
$complies = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( !$complies ) {
|
||||
self::set_default_capabilities();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Guarded sem_get() wrapper.
|
||||
*
|
||||
* @see sem_get()
|
||||
*
|
||||
* @param int $key
|
||||
* @param number $max_acquire
|
||||
* @param number $perm
|
||||
* @param number $auto_release
|
||||
* @return boolean|resource
|
||||
*/
|
||||
private static function sem_get( $key, $max_acquire = 1, $perm = 0666, $auto_release = 1 ) {
|
||||
$result = false;
|
||||
if ( function_exists( 'sem_get' ) ) {
|
||||
$result = sem_get( $key, $max_acquire, $perm, $auto_release );
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Guarded sem_acquire() wrapper.
|
||||
*
|
||||
* To maintain backwards-compatibility with servers running PHP < 5.6 where
|
||||
* the second parameter to sem_acquire() is not supported, we use sem_remove()
|
||||
* and have any calls waiting on sem_acquire() fail silently (achieving that
|
||||
* the activation, update or deactivation routines are not run for those
|
||||
* processes that have been waiting and which would have duplicated execution
|
||||
* unnecessarily).
|
||||
*
|
||||
* @see sem_acquire()
|
||||
*
|
||||
* @param resource $sem_identifier
|
||||
* @param string $nowait (only taken into account and effective on PHP >= 5.6.1)
|
||||
* @return boolean
|
||||
*/
|
||||
private static function sem_acquire( $sem_identifier, $nowait = false ) {
|
||||
$result = false;
|
||||
if ( function_exists( 'sem_acquire' ) ) {
|
||||
if ( version_compare( phpversion(), '5.6.1' ) >= 0 ) {
|
||||
$result = @sem_acquire( $sem_identifier, $nowait );
|
||||
} else {
|
||||
$result = @sem_acquire( $sem_identifier );
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Guarded sem_release() wrapper.
|
||||
*
|
||||
* @see sem_release()
|
||||
*
|
||||
* @param resource $sem_identifier
|
||||
* @return boolean
|
||||
*/
|
||||
private static function sem_release( $sem_identifier ) {
|
||||
$result = false;
|
||||
if ( function_exists( 'sem_release' ) ) {
|
||||
$result = @sem_release( $sem_identifier );
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Guarded sem_remove() wrapper.
|
||||
*
|
||||
* @see sem_remove()
|
||||
*
|
||||
* @param unknown $sem_identifier
|
||||
* @return boolean
|
||||
*/
|
||||
private static function sem_remove( $sem_identifier ) {
|
||||
$result = false;
|
||||
if ( function_exists( 'sem_remove' ) ) {
|
||||
$result = @sem_remove( $sem_identifier );
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Produces a file-based key for use with sem_get().
|
||||
*
|
||||
* @return number
|
||||
*/
|
||||
private static function get_sem_key() {
|
||||
$key = -1;
|
||||
if ( function_exists( 'ftok' ) ) {
|
||||
$key = ftok( __FILE__, 'g' );
|
||||
}
|
||||
if ( $key == -1 ) {
|
||||
$key = fileinode( __FILE__ );
|
||||
}
|
||||
return $key;
|
||||
}
|
||||
}
|
||||
Groups_Controller::boot();
|
||||
@@ -0,0 +1,155 @@
|
||||
<?php
|
||||
/**
|
||||
* class-groups-group-capability.php
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.0.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Group Capability OPM
|
||||
*/
|
||||
class Groups_Group_Capability {
|
||||
|
||||
/**
|
||||
* Hook into appropriate actions when needed.
|
||||
* For now, this does nothing.
|
||||
*
|
||||
* @see Groups_Group::delete()
|
||||
*/
|
||||
public static function init() {
|
||||
// Note that group-capabilities are deleted when a group is deleted.
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist a group-capability relation.
|
||||
*
|
||||
* @param array $map attributes - must provide group_id and capability_id
|
||||
* @return true on success, otherwise false
|
||||
*/
|
||||
public static function create( $map ) {
|
||||
|
||||
global $wpdb;
|
||||
extract( $map );
|
||||
$result = false;
|
||||
|
||||
// avoid nonsense requests
|
||||
if ( !empty( $group_id ) && !empty( $capability_id) ) {
|
||||
// make sure group and capability exist
|
||||
if ( Groups_Group::read( $group_id ) && Groups_Capability::read( $capability_id ) ) {
|
||||
$group_capability_table = _groups_get_tablename( 'group_capability' );
|
||||
// don't try to create duplicate entries
|
||||
// also it would raise an error for duplicate PK
|
||||
if ( 0 === intval( $wpdb->get_var( $wpdb->prepare(
|
||||
"SELECT COUNT(*) FROM $group_capability_table WHERE group_id = %d AND capability_id = %d",
|
||||
Groups_Utility::id( $group_id ),
|
||||
Groups_Utility::id( $capability_id )
|
||||
) ) ) ) {
|
||||
$data = array(
|
||||
'group_id' => Groups_Utility::id( $group_id ),
|
||||
'capability_id' => Groups_Utility::id( $capability_id )
|
||||
);
|
||||
$formats = array( '%d', '%d' );
|
||||
if ( $wpdb->insert( $group_capability_table, $data, $formats ) ) {
|
||||
$result = true;
|
||||
do_action( 'groups_created_group_capability', $group_id, $capability_id );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a group-capability relation.
|
||||
*
|
||||
* @param int $group_id group's id
|
||||
* @param int $capability_id capability's id
|
||||
* @return object upon success, otherwise false
|
||||
*/
|
||||
public static function read( $group_id, $capability_id ) {
|
||||
global $wpdb;
|
||||
$result = false;
|
||||
|
||||
$group_capability_table = _groups_get_tablename( 'group_capability' );
|
||||
$group_capability = $wpdb->get_row( $wpdb->prepare(
|
||||
"SELECT * FROM $group_capability_table WHERE group_id = %d AND capability_id = %d",
|
||||
Groups_Utility::id( $group_id ),
|
||||
Groups_Utility::id( $capability_id )
|
||||
) );
|
||||
if ( $group_capability !== null ) {
|
||||
$result = $group_capability;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update group-capability relation.
|
||||
*
|
||||
* This changes nothing so as of now it's pointless to even call this.
|
||||
*
|
||||
* @param array $map
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
public static function update( $map ) {
|
||||
$result = false;
|
||||
if ( !empty( $group_id ) && !empty( $capability_id) ) {
|
||||
// make sure group and capability exist
|
||||
if ( Groups_Group::read( $group_id ) && Groups_Capability::read( $capability_id ) ) {
|
||||
$result = true;
|
||||
do_action( 'groups_updated_group_capability', $group_id, $capability_id );
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove group-capability relation.
|
||||
*
|
||||
* @param int $group_id
|
||||
* @param int $capability_id
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
public static function delete( $group_id, $capability_id ) {
|
||||
|
||||
global $wpdb;
|
||||
$result = false;
|
||||
|
||||
// avoid nonsense requests
|
||||
if ( !empty( $group_id ) && !empty( $capability_id) ) {
|
||||
// we can omit checking if the group and capability exist, to
|
||||
// allow resolving the relationship after they have been deleted
|
||||
$group_capability_table = _groups_get_tablename( 'group_capability' );
|
||||
// get rid of it
|
||||
$rows = $wpdb->query( $wpdb->prepare(
|
||||
"DELETE FROM $group_capability_table WHERE group_id = %d AND capability_id = %d",
|
||||
Groups_Utility::id( $group_id ),
|
||||
Groups_Utility::id( $capability_id )
|
||||
) );
|
||||
// must have affected a row, otherwise no great success
|
||||
$result = ( $rows !== false ) && ( $rows > 0 );
|
||||
if ( $result ) {
|
||||
do_action( 'groups_deleted_group_capability', $group_id, $capability_id );
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
Groups_Group_Capability::init();
|
||||
@@ -0,0 +1,722 @@
|
||||
<?php
|
||||
/**
|
||||
* class-groups-group.php
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.0.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
require_once( GROUPS_CORE_LIB . '/interface-i-capable.php' );
|
||||
|
||||
/**
|
||||
* Group OPM.
|
||||
*/
|
||||
class Groups_Group implements I_Capable {
|
||||
|
||||
const CACHE_GROUP = 'groups';
|
||||
const READ_GROUP_BY_ID = 'read_group_by_id';
|
||||
const READ_BY_NAME = 'read_by_name';
|
||||
|
||||
/**
|
||||
* @var Object Persisted group.
|
||||
*/
|
||||
var $group = null;
|
||||
|
||||
/**
|
||||
* Create by group id.
|
||||
* Must have been persisted.
|
||||
* @param int $group_id
|
||||
*/
|
||||
public function __construct( $group_id ) {
|
||||
$this->group = self::read( $group_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a property by name.
|
||||
*
|
||||
* Possible properties:
|
||||
* - group_id
|
||||
* - parent_id
|
||||
* - creator_id
|
||||
* - datetime
|
||||
* - name
|
||||
* - description
|
||||
* - capabilities, returns an array of Groups_Capability
|
||||
* - users, returns an array of Groups_User
|
||||
*
|
||||
* @param string $name property's name
|
||||
* @return property value, will return null if property does not exist
|
||||
*/
|
||||
public function __get( $name ) {
|
||||
global $wpdb;
|
||||
$result = null;
|
||||
if ( $this->group !== null ) {
|
||||
switch( $name ) {
|
||||
case 'group_id' :
|
||||
case 'parent_id' :
|
||||
case 'creator_id' :
|
||||
case 'datetime' :
|
||||
case 'name' :
|
||||
case 'description' :
|
||||
$result = $this->group->$name;
|
||||
break;
|
||||
case 'capabilities' :
|
||||
$group_capability_table = _groups_get_tablename( 'group_capability' );
|
||||
$rows = $wpdb->get_results( $wpdb->prepare(
|
||||
"SELECT capability_id FROM $group_capability_table WHERE group_id = %d",
|
||||
Groups_Utility::id( $this->group->group_id )
|
||||
) );
|
||||
if ( $rows ) {
|
||||
$result = array();
|
||||
foreach ( $rows as $row ) {
|
||||
$result[] = new Groups_Capability( $row->capability_id );
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'capabilities_deep' :
|
||||
$capability_ids = $this->capability_ids_deep;
|
||||
$result = array();
|
||||
foreach( $capability_ids as $capability_id ) {
|
||||
$result[] = new Groups_Capability( $capability_id );
|
||||
}
|
||||
break;
|
||||
case 'capability_ids_deep' :
|
||||
$capability_ids = array();
|
||||
$group_table = _groups_get_tablename( 'group' );
|
||||
$group_capability_table = _groups_get_tablename( "group_capability" );
|
||||
// Find this group's and all its parent groups' capabilities.
|
||||
$group_ids = array( Groups_Utility::id( $this->group->group_id ) );
|
||||
$iterations = 0;
|
||||
$old_group_ids_count = 0;
|
||||
$all_groups = $wpdb->get_var( "SELECT COUNT(*) FROM $group_table" );
|
||||
while( ( $iterations < $all_groups ) && ( count( $group_ids ) !== $old_group_ids_count ) ) {
|
||||
$iterations++;
|
||||
$old_group_ids_count = count( $group_ids );
|
||||
$id_list = implode( ',', $group_ids );
|
||||
$parent_group_ids = $wpdb->get_results(
|
||||
"SELECT parent_id FROM $group_table WHERE parent_id IS NOT NULL AND group_id IN ($id_list)"
|
||||
);
|
||||
if ( $parent_group_ids ) {
|
||||
foreach( $parent_group_ids as $parent_group_id ) {
|
||||
$parent_group_id = Groups_Utility::id( $parent_group_id->parent_id );
|
||||
if ( !in_array( $parent_group_id, $group_ids ) ) {
|
||||
$group_ids[] = $parent_group_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( count( $group_ids ) > 0 ) {
|
||||
$id_list = implode( ',', $group_ids );
|
||||
$rows = $wpdb->get_results(
|
||||
"SELECT DISTINCT capability_id FROM $group_capability_table WHERE group_id IN ($id_list)"
|
||||
);
|
||||
if ( $rows ) {
|
||||
foreach ( $rows as $row ) {
|
||||
$capability_ids[] = $row->capability_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
$result = $capability_ids;
|
||||
break;
|
||||
case 'users' :
|
||||
$user_group_table = _groups_get_tablename( 'user_group' );
|
||||
$users = $wpdb->get_results( $wpdb->prepare(
|
||||
"SELECT $wpdb->users.* FROM $wpdb->users LEFT JOIN $user_group_table ON $wpdb->users.ID = $user_group_table.user_id WHERE $user_group_table.group_id = %d",
|
||||
Groups_Utility::id( $this->group->group_id )
|
||||
) );
|
||||
if ( $users ) {
|
||||
$result = array();
|
||||
foreach( $users as $user ) {
|
||||
$groups_user = new Groups_User();
|
||||
$groups_user->user = new WP_User( $user );
|
||||
$result[] = $groups_user;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'user_ids' :
|
||||
$user_group_table = _groups_get_tablename( 'user_group' );
|
||||
$user_ids = $wpdb->get_results( $wpdb->prepare(
|
||||
"SELECT $wpdb->users.ID FROM $wpdb->users LEFT JOIN $user_group_table ON $wpdb->users.ID = $user_group_table.user_id WHERE $user_group_table.group_id = %d",
|
||||
Groups_Utility::id( $this->group->group_id )
|
||||
) );
|
||||
if ( $user_ids ) {
|
||||
$result = array();
|
||||
foreach( $user_ids as $user_id ) {
|
||||
$result[] = $user_id->ID;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* (non-PHPdoc)
|
||||
* @see I_Capable::can()
|
||||
*/
|
||||
public function can( $capability ) {
|
||||
|
||||
global $wpdb;
|
||||
$result = false;
|
||||
|
||||
if ( $this->group !== null ) {
|
||||
|
||||
$group_table = _groups_get_tablename( 'group' );
|
||||
$capability_table = _groups_get_tablename( 'capability' );
|
||||
$group_capability_table = _groups_get_tablename( 'group_capability' );
|
||||
|
||||
// determine capability id
|
||||
$capability_id = null;
|
||||
if ( is_numeric( $capability ) ) {
|
||||
$capability_id = Groups_Utility::id( $capability );
|
||||
} else if ( is_string( $capability ) ) {
|
||||
$capability_id = $wpdb->get_var( $wpdb->prepare(
|
||||
"SELECT capability_id FROM $capability_table WHERE capability = %s",
|
||||
$capability
|
||||
) );
|
||||
}
|
||||
|
||||
if ( $capability_id !== null ) {
|
||||
// check if the group itself can
|
||||
$result = ( Groups_Group_Capability::read( $this->group->group_id, $capability_id ) !== false );
|
||||
if ( !$result ) {
|
||||
// find all parent groups and include in the group's
|
||||
// upward hierarchy to see if any of these can
|
||||
$group_ids = array( $this->group->group_id );
|
||||
$iterations = 0;
|
||||
$old_group_ids_count = 0;
|
||||
$all_groups = $wpdb->get_var( "SELECT COUNT(*) FROM $group_table" );
|
||||
while( ( $iterations < $all_groups ) && ( count( $group_ids ) !== $old_group_ids_count ) ) {
|
||||
$iterations++;
|
||||
$old_group_ids_count = count( $group_ids );
|
||||
$id_list = implode( ',', $group_ids );
|
||||
$parent_group_ids = $wpdb->get_results(
|
||||
"SELECT parent_id FROM $group_table WHERE parent_id IS NOT NULL AND group_id IN ($id_list)"
|
||||
);
|
||||
if ( $parent_group_ids ) {
|
||||
foreach( $parent_group_ids as $parent_group_id ) {
|
||||
$parent_group_id = Groups_Utility::id( $parent_group_id->parent_id );
|
||||
if ( !in_array( $parent_group_id, $group_ids ) ) {
|
||||
$group_ids[] = $parent_group_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( count( $group_ids ) > 0 ) {
|
||||
$id_list = implode( ',', $group_ids );
|
||||
$rows = $wpdb->get_results( $wpdb->prepare(
|
||||
"SELECT capability_id FROM $group_capability_table WHERE capability_id = %d AND group_id IN ($id_list)",
|
||||
Groups_Utility::id( $capability_id )
|
||||
) );
|
||||
|
||||
if ( count( $rows ) > 0 ) {
|
||||
$result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$result = apply_filters_ref_array( 'groups_group_can', array( $result, &$this, $capability ) );
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist a group.
|
||||
*
|
||||
* Parameters:
|
||||
* - name (required) - the group's name
|
||||
* - creator_id (optional) - defaults to the current user's id
|
||||
* - datetime (optional) - defaults to now
|
||||
* - description (optional)
|
||||
* - parent_id (optional)
|
||||
*
|
||||
* @param array $map attributes
|
||||
* @return group_id on success, otherwise false
|
||||
*/
|
||||
public static function create( $map ) {
|
||||
global $wpdb;
|
||||
extract( $map );
|
||||
$result = false;
|
||||
$error = false;
|
||||
|
||||
if ( !empty( $name ) ) {
|
||||
|
||||
$group_table = _groups_get_tablename( 'group' );
|
||||
|
||||
$data = array( 'name' => $name );
|
||||
$formats = array( '%s' );
|
||||
if ( !isset( $creator_id ) ) {
|
||||
$creator_id = get_current_user_id();
|
||||
}
|
||||
if ( isset( $creator_id ) ) {
|
||||
$data['creator_id'] = Groups_Utility::id( $creator_id );
|
||||
$formats[] = '%d';
|
||||
}
|
||||
if ( !isset( $datetime ) ) {
|
||||
$datetime = date( 'Y-m-d H:i:s', time() );
|
||||
}
|
||||
if ( isset( $datetime ) ) {
|
||||
$data['datetime'] = $datetime;
|
||||
$formats[] = '%s';
|
||||
}
|
||||
if ( !empty( $description ) ) {
|
||||
$data['description'] = $description;
|
||||
$formats[] = '%s';
|
||||
}
|
||||
if ( !empty( $parent_id ) ) {
|
||||
// only allow to set an existing parent group (that is from the same blog)
|
||||
$parent_group_id = $wpdb->get_var( $wpdb->prepare(
|
||||
"SELECT group_id FROM $group_table WHERE group_id = %d",
|
||||
Groups_Utility::id( $parent_id )
|
||||
) );
|
||||
if ( $parent_group_id === $parent_id ) {
|
||||
$data['parent_id'] = Groups_Utility::id( $parent_id );
|
||||
$formats[] = '%d';
|
||||
} else {
|
||||
$error = true;
|
||||
}
|
||||
}
|
||||
// no duplicate names
|
||||
$duplicate = Groups_Group::read_by_name( $name );
|
||||
if ( $duplicate ) {
|
||||
$error = true;
|
||||
}
|
||||
if ( !$error ) {
|
||||
if ( $wpdb->insert( $group_table, $data, $formats ) ) {
|
||||
if ( $result = $wpdb->get_var( "SELECT LAST_INSERT_ID()" ) ) {
|
||||
// must clear cache for this name in case it has been requested previously as it now exists
|
||||
Groups_Cache::delete( self::READ_BY_NAME . '_' . $name, self::CACHE_GROUP );
|
||||
do_action( 'groups_created_group', $result );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a group.
|
||||
*
|
||||
* @param int $group_id group's id
|
||||
* @return object upon success, otherwise false
|
||||
*/
|
||||
public static function read( $group_id ) {
|
||||
global $wpdb;
|
||||
$result = false;
|
||||
$cached = Groups_Cache::get( self::READ_GROUP_BY_ID . '_' . $group_id, self::CACHE_GROUP );
|
||||
if ( $cached !== null ) {
|
||||
$result = $cached->value;
|
||||
unset( $cached );
|
||||
} else {
|
||||
$group_table = _groups_get_tablename( 'group' );
|
||||
$group = $wpdb->get_row( $wpdb->prepare(
|
||||
"SELECT * FROM $group_table WHERE group_id = %d",
|
||||
Groups_Utility::id( $group_id )
|
||||
) );
|
||||
if ( isset( $group->group_id ) ) {
|
||||
$result = $group;
|
||||
}
|
||||
Groups_Cache::set( self::READ_GROUP_BY_ID . '_' . $group_id, $result, self::CACHE_GROUP );
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a group by name.
|
||||
*
|
||||
* @param string $name the group's name
|
||||
* @return object upon success, otherwise false
|
||||
*/
|
||||
public static function read_by_name( $name ) {
|
||||
global $wpdb;
|
||||
$cached = Groups_Cache::get( self::READ_BY_NAME . '_' . $name, self::CACHE_GROUP );
|
||||
if ( $cached !== null ) {
|
||||
$result = $cached->value;
|
||||
unset( $cached );
|
||||
} else {
|
||||
$result = false;
|
||||
$group_table = _groups_get_tablename( 'group' );
|
||||
$group = $wpdb->get_row( $wpdb->prepare(
|
||||
"SELECT * FROM $group_table WHERE name = %s",
|
||||
$name
|
||||
) );
|
||||
if ( isset( $group->group_id ) ) {
|
||||
$result = $group;
|
||||
}
|
||||
Groups_Cache::set( self::READ_BY_NAME . '_' . $name, $result, self::CACHE_GROUP );
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update group.
|
||||
*
|
||||
* @param array $map group attribute, must contain group_id
|
||||
* @return group_id on success, otherwise false
|
||||
*/
|
||||
public static function update( $map ) {
|
||||
|
||||
global $wpdb;
|
||||
extract( $map );
|
||||
$result = false;
|
||||
|
||||
if ( isset( $group_id ) && !empty( $name ) ) {
|
||||
$old_group = Groups_Group::read( $group_id );
|
||||
$group_table = _groups_get_tablename( 'group' );
|
||||
if ( !isset( $description ) || ( $description === null ) ) {
|
||||
$description = '';
|
||||
}
|
||||
$wpdb->query( $wpdb->prepare(
|
||||
"UPDATE $group_table SET name = %s, description = %s WHERE group_id = %d",
|
||||
$name,
|
||||
$description,
|
||||
Groups_Utility::id( $group_id )
|
||||
) );
|
||||
if ( empty( $parent_id ) ) {
|
||||
$wpdb->query( $wpdb->prepare(
|
||||
"UPDATE $group_table SET parent_id = NULL WHERE group_id = %d",
|
||||
Groups_Utility::id( $group_id )
|
||||
) );
|
||||
} else {
|
||||
|
||||
// Prohibit circular dependencies:
|
||||
// This group cannot have a parent that is its successor
|
||||
// at any level in its successor hierarchy.
|
||||
// S(g) : successor of group g
|
||||
// S*(g) : successors of group g, any level deep
|
||||
// P(g) : parent of g
|
||||
// ---
|
||||
// It must hold: !( P(g) in S*(g) )
|
||||
|
||||
// Find all successors of this group
|
||||
$groups = $wpdb->get_var( "SELECT COUNT(*) FROM $group_table" );
|
||||
if ( $groups !== null ) {
|
||||
$group_ids = array();
|
||||
$group_ids[] = Groups_Utility::id( $group_id );
|
||||
$iterations = 0;
|
||||
$old_group_ids_count = 0;
|
||||
while( ( $iterations < $groups ) && ( count( $group_ids ) > 0 ) && ( count( $group_ids ) !== $old_group_ids_count ) ) {
|
||||
|
||||
$iterations++;
|
||||
$old_group_ids_count = count( $group_ids );
|
||||
|
||||
$id_list = implode( ',', $group_ids );
|
||||
// We can trust ourselves here, no need to use prepare()
|
||||
// but careful if this query is modified!
|
||||
$successor_group_ids = $wpdb->get_results(
|
||||
"SELECT group_id FROM $group_table WHERE parent_id IS NOT NULL AND parent_id IN ($id_list)"
|
||||
);
|
||||
if ( $successor_group_ids ) {
|
||||
foreach( $successor_group_ids as $successor_group_id ) {
|
||||
$successor_group_id = Groups_Utility::id( $successor_group_id->group_id );
|
||||
if ( !in_array( $successor_group_id, $group_ids ) ) {
|
||||
$group_ids[] = $successor_group_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// only add if condition holds
|
||||
if ( !in_array( Groups_Utility::id( $parent_id ), $group_ids ) ) {
|
||||
$wpdb->query( $wpdb->prepare(
|
||||
"UPDATE $group_table SET parent_id = %d WHERE group_id = %d",
|
||||
Groups_Utility::id( $parent_id),
|
||||
Groups_Utility::id( $group_id )
|
||||
) );
|
||||
}
|
||||
}
|
||||
}
|
||||
$result = $group_id;
|
||||
if ( !empty( $group_id ) ) {
|
||||
Groups_Cache::delete( self::READ_GROUP_BY_ID . '_' . $group_id, self::CACHE_GROUP );
|
||||
}
|
||||
if ( !empty( $name ) ) {
|
||||
Groups_Cache::delete( self::READ_BY_NAME . '_' . $name, self::CACHE_GROUP );
|
||||
}
|
||||
if ( !empty( $old_group ) && !empty( $old_group->name ) ) {
|
||||
Groups_Cache::delete( self::READ_BY_NAME . '_' . $old_group->name, self::CACHE_GROUP );
|
||||
}
|
||||
do_action( 'groups_updated_group', $result );
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove group and its relations.
|
||||
*
|
||||
* @param int $group_id
|
||||
* @return group_id if successful, false otherwise
|
||||
*/
|
||||
public static function delete( $group_id ) {
|
||||
|
||||
global $wpdb;
|
||||
$result = false;
|
||||
|
||||
if ( $group = self::read( $group_id ) ) {
|
||||
|
||||
// delete group-capabilities
|
||||
$group_capability_table = _groups_get_tablename( 'group_capability' );
|
||||
$wpdb->query( $wpdb->prepare(
|
||||
"DELETE FROM $group_capability_table WHERE group_id = %d",
|
||||
Groups_Utility::id( $group->group_id )
|
||||
) );
|
||||
|
||||
// delete group-users
|
||||
$user_group_table = _groups_get_tablename( 'user_group' );
|
||||
$wpdb->query( $wpdb->prepare(
|
||||
"DELETE FROM $user_group_table WHERE group_id = %d",
|
||||
$group->group_id
|
||||
) );
|
||||
|
||||
// set parent_id to null where this group is parent
|
||||
$group_table = _groups_get_tablename( 'group' );
|
||||
$wpdb->query( $wpdb->prepare(
|
||||
"UPDATE $group_table SET parent_id = NULL WHERE parent_id = %d",
|
||||
$group->group_id
|
||||
) );
|
||||
|
||||
// delete group
|
||||
if ( $wpdb->query( $wpdb->prepare(
|
||||
"DELETE FROM $group_table WHERE group_id = %d",
|
||||
$group->group_id
|
||||
) ) ) {
|
||||
$result = $group->group_id;
|
||||
if ( !empty( $group->group_id ) ) {
|
||||
Groups_Cache::delete( self::READ_GROUP_BY_ID . '_' . $group_id, self::CACHE_GROUP );
|
||||
}
|
||||
if ( !empty( $group->name ) ) {
|
||||
Groups_Cache::delete( self::READ_BY_NAME . '_' . $group->name, self::CACHE_GROUP );
|
||||
}
|
||||
do_action( 'groups_deleted_group', $result );
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of group IDs.
|
||||
*
|
||||
* If no arguments are passed, IDs for all existing groups are returned.
|
||||
*
|
||||
* @param array $args
|
||||
* - ['order_by'] string a Groups_Group property
|
||||
* - ['order'] string ASC or DESC. Only applied if 'order_by' is set.
|
||||
* - ['parent_id'] int retrieve groups whose parent is indicated by this ID
|
||||
* - ['include'] array|string with one or more IDs of groups to include, separated by comma
|
||||
* - ['include_by_name'] array|string with one ore more group names of groups to include, separated by comma
|
||||
* - ['exclude'] array|string with one or more IDs of groups to exclude, separated by comma
|
||||
* - ['exclude_by_name'] array|string with one ore more group names of groups to exclude, separated by comma
|
||||
*
|
||||
* @return array of int with group IDs
|
||||
*
|
||||
* @since groups 1.4.9
|
||||
*/
|
||||
public static function get_group_ids( $args = array() ) {
|
||||
$result = array();
|
||||
$args['fields'] = 'group_id';
|
||||
$groups = self::get_groups( $args );
|
||||
if ( sizeof( $groups ) > 0 ) {
|
||||
foreach ( $groups as $group ) {
|
||||
$result[] = $group->group_id;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of database results by querying the group table.
|
||||
*
|
||||
* @param Array $args
|
||||
* - ['fields'] string with fields to get separated by comma. If empty then get all fields.
|
||||
* - ['order_by'] string a Groups_Group property
|
||||
* - ['order'] string ASC or DESC. Only applied if 'order_by' is set.
|
||||
* - ['parent_id'] int retrieve groups whose parent is indicated by this ID
|
||||
* - ['include'] array|string with one or more IDs of groups to include, separated by comma
|
||||
* - ['include_by_name'] array|string with one ore more group names of groups to include, separated by comma
|
||||
* - ['exclude'] array|string with one or more IDs of groups to exclude, separated by comma
|
||||
* - ['exclude_by_name'] array|string with one ore more group names of groups to exclude, separated by comma
|
||||
*
|
||||
* @return array of object with query rows
|
||||
*
|
||||
* @since groups 1.4.9
|
||||
*/
|
||||
public static function get_groups( $args = array() ) {
|
||||
global $wpdb;
|
||||
|
||||
extract( $args );
|
||||
|
||||
if ( !isset( $fields ) ) {
|
||||
$fields = '*';
|
||||
} else {
|
||||
$array_fields = explode( ',', sanitize_text_field( $fields ) );
|
||||
$fields = '';
|
||||
foreach ( $array_fields as $field ) {
|
||||
switch( trim( $field ) ) {
|
||||
case 'group_id' :
|
||||
case 'parent_id' :
|
||||
case 'creator_id' :
|
||||
case 'datetime' :
|
||||
case 'name' :
|
||||
case 'description' :
|
||||
$fields .= ',' . trim( $field );
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( strlen( $fields ) > 0 ) {
|
||||
$fields = substr( $fields, 1 );
|
||||
}
|
||||
}
|
||||
|
||||
if ( !isset( $order ) ) {
|
||||
$order = '';
|
||||
} else {
|
||||
$order = strtoupper( sanitize_text_field( trim( $order ) ) );
|
||||
switch( $order ) {
|
||||
case 'ASC' :
|
||||
case 'DESC' :
|
||||
break;
|
||||
default :
|
||||
$order = 'ASC';
|
||||
}
|
||||
}
|
||||
|
||||
if ( !isset( $order_by ) ) {
|
||||
$order_by = '';
|
||||
} else {
|
||||
$order_by = sanitize_text_field( $order_by );
|
||||
switch( trim( $order_by ) ) {
|
||||
case 'group_id' :
|
||||
case 'parent_id' :
|
||||
case 'creator_id' :
|
||||
case 'datetime' :
|
||||
case 'name' :
|
||||
case 'description' :
|
||||
$order_by = " ORDER BY $order_by $order "; // Watch out! This is unescaped but safe within this switch.
|
||||
break;
|
||||
default :
|
||||
$order_by = '';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$where = '';
|
||||
if ( isset( $parent_id ) ) {
|
||||
$parent_id = sanitize_text_field( $parent_id );
|
||||
if ( is_numeric ( $parent_id ) ) {
|
||||
$where .= $wpdb->prepare( " WHERE parent_id=%s ", array( $parent_id ) );
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// include by group ID
|
||||
//
|
||||
$where_include = '';
|
||||
$include = !empty( $include ) ? $include : null;
|
||||
if ( !empty( $include ) && !is_array( $include ) && is_string( $include ) ) {
|
||||
$include = explode( ',', $include );
|
||||
}
|
||||
if ( $include !== null && count( $include ) > 0 ) {
|
||||
$include = implode( ',', array_map( 'intval', array_map( 'trim', $include ) ) );
|
||||
if ( strlen( $include ) > 0 ) {
|
||||
$where_include = " group_id IN ($include) ";
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// include by group name
|
||||
//
|
||||
$where_include_by_name = '';
|
||||
$include_by_name = !empty( $include_by_name ) ? $include_by_name : null;
|
||||
if ( !empty( $include_by_name ) && !is_array( $include_by_name ) && is_string( $include_by_name ) ) {
|
||||
$include_by_name = explode( ',', $include_by_name );
|
||||
}
|
||||
if ( $include_by_name !== null && count( $include_by_name ) > 0 ) {
|
||||
$include_by_name = "'" . implode( "','", array_map( 'esc_sql', array_map( 'trim', $include_by_name ) ) ) . "'";
|
||||
if ( strlen( $include_by_name ) > 0 ) {
|
||||
$where_include_by_name = " name IN ($include_by_name) ";
|
||||
}
|
||||
}
|
||||
|
||||
// adding includes ...
|
||||
if ( ( $where_include !== '' ) || ( $where_include_by_name !== '' ) ) {
|
||||
if ( $where == '' ) {
|
||||
$where .= " WHERE ";
|
||||
} else {
|
||||
$where .= " AND ";
|
||||
}
|
||||
}
|
||||
if ( ( $where_include !== "" ) && ( $where_include_by_name !== "" ) ) {
|
||||
$where .= "(";
|
||||
}
|
||||
if ( $where_include !== "" ) {
|
||||
$where .= $where_include;
|
||||
}
|
||||
if ( ( $where_include !== "" ) && ( $where_include_by_name !== "" ) ) {
|
||||
$where .= " OR ";
|
||||
}
|
||||
if ( $where_include_by_name !== "" ) {
|
||||
$where .= $where_include_by_name;
|
||||
}
|
||||
if ( ( $where_include !== "" ) && ( $where_include_by_name !== "" ) ) {
|
||||
$where .= ")";
|
||||
}
|
||||
|
||||
//
|
||||
// exclude
|
||||
//
|
||||
$exclude = !empty( $exclude ) ? $exclude : null;
|
||||
if ( !empty( $exclude ) && !is_array( $exclude ) && is_string( $exclude ) ) {
|
||||
$exclude = explode( ',', $exclude );
|
||||
}
|
||||
if ( $exclude !== null && count( $exclude ) > 0 ) {
|
||||
$exclude = implode( ',', array_map( 'intval', array_map( 'trim', $exclude ) ) );
|
||||
if ( strlen( $exclude ) > 0 ) {
|
||||
if ( empty( $where ) ) {
|
||||
$where = " WHERE group_id NOT IN ($exclude) ";
|
||||
} else {
|
||||
$where .= " AND group_id NOT IN ($exclude) ";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// exclude by group name
|
||||
//
|
||||
$exclude_by_name = !empty( $exclude_by_name ) ? $exclude_by_name : null;
|
||||
if ( !empty( $exclude_by_name ) && !is_array( $exclude_by_name ) && is_string( $exclude_by_name ) ) {
|
||||
$exclude_by_name = explode( ',', $exclude_by_name );
|
||||
}
|
||||
if ( $exclude_by_name !== null && count( $exclude_by_name ) > 0 ) {
|
||||
$exclude_by_name = "'" . implode( "','", array_map( 'esc_sql', array_map( 'trim', $exclude_by_name ) ) ) . "'";
|
||||
if ( strlen( $exclude_by_name ) > 0 ) {
|
||||
if ( empty( $where ) ) {
|
||||
$where = " WHERE name NOT IN ($exclude_by_name) ";
|
||||
} else {
|
||||
$where .= " AND name NOT IN ($exclude_by_name) ";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$groups_table = _groups_get_tablename( 'group' );
|
||||
$groups = $wpdb->get_results( "SELECT $fields FROM $groups_table $where $order_by" );
|
||||
|
||||
return $groups;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
<?php
|
||||
/**
|
||||
* class-groups-help.php
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.0.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Help renderer.
|
||||
*/
|
||||
class Groups_Help {
|
||||
|
||||
/**
|
||||
* Help setup.
|
||||
*/
|
||||
public static function init() {
|
||||
add_action( 'groups_admin_menu', array( __CLASS__, 'groups_admin_menu' ) );
|
||||
// @todo temporary fix for GFA <= 1.0.11 on localized installations - can be removed when all are updated
|
||||
if ( defined( 'GFA_VIEWS_LIB' ) ) {
|
||||
if ( file_exists( GFA_VIEWS_LIB . '/class-gfa-help.php' ) ) {
|
||||
include_once( GFA_VIEWS_LIB . '/class-gfa-help.php' );
|
||||
}
|
||||
}
|
||||
|
||||
add_filter( 'admin_footer_text', array( __CLASS__, 'admin_footer_text' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds contextual help to Groups admin screens.
|
||||
*
|
||||
* @param array $pages admin pages
|
||||
*/
|
||||
public static function groups_admin_menu( $pages ) {
|
||||
foreach ( $pages as $page ) {
|
||||
add_action( 'load-' . $page, array( __CLASS__, 'contextual_help' ) );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds help to an admin screen.
|
||||
*/
|
||||
public static function contextual_help() {
|
||||
if ( $screen = get_current_screen() ) {
|
||||
$show_groups_help = false;
|
||||
$help_title = __( 'Groups', 'groups' );
|
||||
$screen_id = $screen->base;
|
||||
// The prefix of the $screen_id is translated, use only the suffix
|
||||
// to identify a screen:
|
||||
$ids = array(
|
||||
'groups-admin' => __( 'Groups', 'groups' ),
|
||||
'groups-admin-groups' => __( 'Groups', 'groups' ),
|
||||
'groups-admin-options' => __( 'Options', 'groups' ),
|
||||
'groups-admin-capabilities' => __( 'Capabilities', 'groups' ),
|
||||
);
|
||||
foreach ( $ids as $id => $title ) {
|
||||
$i = strpos( $screen_id, $id );
|
||||
if ( $i !== false ) {
|
||||
if ( $i + strlen( $id ) == strlen( $screen_id ) ) {
|
||||
$screen_id = $id;
|
||||
$show_groups_help = true;
|
||||
$help_title = $title;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( $show_groups_help ) {
|
||||
$help = '<h3><a href="http://www.itthinx.com/plugins/groups" target="_blank">'. $help_title .'</a></h3>';
|
||||
$help .= '<p>';
|
||||
$help .= __( 'The complete documentation is available on the <a href="http://docs.itthinx.com/document/groups">Documentation</a> pages for Groups.', 'groups' );
|
||||
$help .= '</p>';
|
||||
switch ( $screen_id ) {
|
||||
case 'groups-admin' :
|
||||
case 'groups-admin-groups':
|
||||
$help .= '<p>' . __( 'Here you can <strong>add</strong>, <strong>edit</strong> and <strong>remove</strong> groups.', 'groups' ) . '</p>';
|
||||
break;
|
||||
case 'groups-admin-options' :
|
||||
case 'groups-admin-capabilities' :
|
||||
break;
|
||||
}
|
||||
|
||||
$screen->add_help_tab(
|
||||
array(
|
||||
'id' => $screen_id,
|
||||
'title' => $help_title,
|
||||
'content' => $help
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the footer text for Groups on relevant screens.
|
||||
*
|
||||
* @param string $footer_text
|
||||
* @return mixed
|
||||
*/
|
||||
public static function admin_footer_text( $text ) {
|
||||
if ( function_exists( 'get_current_screen' ) ) {
|
||||
$current_screen = get_current_screen();
|
||||
if (
|
||||
isset( $current_screen->id ) &&
|
||||
(
|
||||
stripos( $current_screen->id, 'groups-' ) === 0 ||
|
||||
stripos( $current_screen->id, 'groups_' ) === 0 ||
|
||||
stripos( $current_screen->id, 'toplevel_page_groups' ) === 0
|
||||
)
|
||||
) {
|
||||
$text = self::footer( false );
|
||||
}
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns or renders the footer.
|
||||
*
|
||||
* @param boolean $render
|
||||
*/
|
||||
public static function footer( $render = true ) {
|
||||
$footer =
|
||||
'<span class="groups-footer">' .
|
||||
__( 'Thank you for using <a href="http://www.itthinx.com/plugins/groups" target="_blank">Groups</a> by <a href="http://www.itthinx.com" target="_blank">itthinx</a>.', 'groups' ) .
|
||||
' ' .
|
||||
sprintf(
|
||||
__( 'Please give it a <a href="%s">★★★★★</a> rating.', 'groups' ),
|
||||
esc_attr( 'http://wordpress.org/support/view/plugin-reviews/groups?filter=5#postform' )
|
||||
) .
|
||||
'</span>';
|
||||
$footer = apply_filters( 'groups_footer', $footer );
|
||||
if ( $render ) {
|
||||
echo $footer;
|
||||
} else {
|
||||
return $footer;
|
||||
}
|
||||
}
|
||||
}
|
||||
Groups_Help::init();
|
||||
@@ -0,0 +1,209 @@
|
||||
<?php
|
||||
/**
|
||||
* class-groups-options.php
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.0.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Groups options handler
|
||||
*/
|
||||
class Groups_Options {
|
||||
|
||||
/**
|
||||
* Groups plugin option key.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const option_key = 'groups_options';
|
||||
|
||||
/**
|
||||
* General option index.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const general = 'general';
|
||||
|
||||
/**
|
||||
* No instances are needed.
|
||||
*/
|
||||
private function __construct() {
|
||||
}
|
||||
|
||||
/**
|
||||
* No cloning.
|
||||
*/
|
||||
private function __clone() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Would be pointless.
|
||||
*/
|
||||
private function __wakeup() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers Groups options (not autoloaded).
|
||||
*/
|
||||
public static function init() {
|
||||
$options = get_option( self::option_key );
|
||||
if ( $options === false ) {
|
||||
$options = array( self::general => array() );
|
||||
add_option( self::option_key, $options, null, 'no' );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current Groups options and initializes them
|
||||
* through init() if needed.
|
||||
* @return array Groups options
|
||||
*/
|
||||
private static function get_options() {
|
||||
$options = get_option( self::option_key );
|
||||
if ( $options === false ) {
|
||||
self::init();
|
||||
$options = get_option( self::option_key );
|
||||
}
|
||||
return $options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of a general setting.
|
||||
*
|
||||
* @param string $option the option id
|
||||
* @param mixed $default default value to retrieve if option is not set
|
||||
* @return option value, $default if set or null
|
||||
*/
|
||||
public static function get_option( $option, $default = null ) {
|
||||
$options = self::get_options();
|
||||
$value = isset( $options[self::general][$option] ) ? $options[self::general][$option] : null;
|
||||
if ( $value === null ) {
|
||||
$value = $default;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the value of a user setting.
|
||||
*
|
||||
* @param string $option the option id
|
||||
* @param mixed $default default value to retrieve if option is not set
|
||||
* @param int $user_id retrieve option for this user, defaults to null for current user
|
||||
* @return option value, $default if set or null
|
||||
*/
|
||||
public static function get_user_option( $option, $default = null, $user_id = null ) {
|
||||
if ( $user_id === null ) {
|
||||
$current_user = wp_get_current_user();
|
||||
if ( !empty( $current_user ) ) {
|
||||
$user_id = $current_user->ID;
|
||||
}
|
||||
}
|
||||
$value = null;
|
||||
if ( $user_id !== null ) {
|
||||
$options = self::get_options();
|
||||
$value = isset( $options[$user_id][$option] ) ? $options[$user_id][$option] : null;
|
||||
}
|
||||
if ( $value === null ) {
|
||||
$value = $default;
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a general setting.
|
||||
*
|
||||
* @param string $option the option's id
|
||||
* @param mixed $new_value the new value
|
||||
*/
|
||||
public static function update_option( $option, $new_value ) {
|
||||
$options = self::get_options();
|
||||
$options[self::general][$option] = $new_value;
|
||||
update_option( self::option_key, $options );
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a user setting.
|
||||
*
|
||||
* @param string $option the option's id
|
||||
* @param mixed $new_value the new value
|
||||
* @param int $user_id update option for this user, defaults to null for current user
|
||||
*/
|
||||
public static function update_user_option( $option, $new_value, $user_id = null ) {
|
||||
|
||||
if ( $user_id === null ) {
|
||||
$current_user = wp_get_current_user();
|
||||
if ( !empty( $current_user ) ) {
|
||||
$user_id = $current_user->ID;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $user_id !== null ) {
|
||||
$options = self::get_options();
|
||||
$options[$user_id][$option] = $new_value;
|
||||
update_option( self::option_key, $options );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a general setting.
|
||||
*
|
||||
* @param string $option the option's id
|
||||
*/
|
||||
public static function delete_option( $option ) {
|
||||
$options = self::get_options();
|
||||
if ( isset( $options[self::general][$option] ) ) {
|
||||
unset( $options[self::general][$option] );
|
||||
update_option( self::option_key, $options );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a user setting.
|
||||
*
|
||||
* @param string $option the option's id
|
||||
* @param int $user_id delete option for this user, defaults to null for current user
|
||||
*/
|
||||
public static function delete_user_option( $option, $user_id = null ) {
|
||||
|
||||
if ( $user_id === null ) {
|
||||
$current_user = wp_get_current_user();
|
||||
if ( !empty( $current_user ) ) {
|
||||
$user_id = $current_user->ID;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $user_id !== null ) {
|
||||
$options = self::get_options();
|
||||
if ( isset( $options[$user_id][$option] ) ) {
|
||||
unset( $options[$user_id][$option] );
|
||||
update_option( self::option_key, $options );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all settings - this includes user and general options.
|
||||
*/
|
||||
public static function flush_options() {
|
||||
delete_option( self::option_key );
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
<?php
|
||||
/**
|
||||
* class-groups-pagination.php
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.0.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Pagination based on WP_List_Table.
|
||||
*/
|
||||
class Groups_Pagination {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param int $total_items how many items there are to display
|
||||
* @param int $total_pages how many pages there are, normally leave set to null
|
||||
* @param int $per_page how many results to show on each page
|
||||
*/
|
||||
function __construct( $total_items, $total_pages, $per_page ) {
|
||||
$this->set_pagination_args(
|
||||
array(
|
||||
'total_items' => $total_items,
|
||||
'total_pages' => $total_pages,
|
||||
'per_page' => $per_page
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current page number
|
||||
* @return int the current page number
|
||||
*/
|
||||
function get_pagenum() {
|
||||
$pagenum = isset( $_REQUEST['paged'] ) ? absint( $_REQUEST['paged'] ) : 0;
|
||||
if ( !isset( $_REQUEST['paged'] ) ) { // needed with rewritten page added
|
||||
if ( preg_match( "/(\/page\/)(\d+)/", $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'], $matches ) ) {
|
||||
$pagenum = absint( $matches[2] );
|
||||
}
|
||||
}
|
||||
|
||||
if( isset( $this->_pagination_args['total_pages'] ) && $pagenum > $this->_pagination_args['total_pages'] ) {
|
||||
$pagenum = $this->_pagination_args['total_pages'];
|
||||
}
|
||||
return max( 1, $pagenum );
|
||||
}
|
||||
|
||||
/**
|
||||
* An internal method that sets all the necessary pagination arguments
|
||||
*
|
||||
* @param array $args An associative array with information about the pagination
|
||||
* @access protected
|
||||
*/
|
||||
function set_pagination_args( $args ) {
|
||||
$args = wp_parse_args( $args, array(
|
||||
'total_items' => 0,
|
||||
'total_pages' => 0,
|
||||
'per_page' => 0,
|
||||
) );
|
||||
|
||||
if ( !$args['total_pages'] && $args['per_page'] > 0 )
|
||||
$args['total_pages'] = ceil( $args['total_items'] / $args['per_page'] );
|
||||
|
||||
$this->_pagination_args = $args;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns or displays the pagination.
|
||||
* @param string $which where it's displayed
|
||||
* @param boolean $echo displays if true, otherwise returns
|
||||
*/
|
||||
function pagination( $which, $echo = false ) {
|
||||
|
||||
if ( empty( $this->_pagination_args ) )
|
||||
return;
|
||||
|
||||
extract( $this->_pagination_args );
|
||||
|
||||
$output = '<span class="displaying-num">' . sprintf( _n( '1 item', '%s items', $total_items ), number_format_i18n( $total_items ) ) . '</span>';
|
||||
|
||||
$current = $this->get_pagenum();
|
||||
|
||||
$current_url = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
|
||||
|
||||
$current_url = remove_query_arg( array( 'hotkeys_highlight_last', 'hotkeys_highlight_first' ), $current_url );
|
||||
|
||||
// needs to remove rewritten added page
|
||||
$current_url = preg_replace( '/\/page\/\d+/', '', $current_url );
|
||||
|
||||
$page_links = array();
|
||||
|
||||
$disable_first = $disable_last = '';
|
||||
if ( $current == 1 )
|
||||
$disable_first = ' disabled';
|
||||
if ( $current == $total_pages )
|
||||
$disable_last = ' disabled';
|
||||
|
||||
$page_links[] = sprintf( '<a class="%s" title="%s" href="%s">%s</a>',
|
||||
'first-page' . $disable_first,
|
||||
esc_attr__( 'Go to the first page' ),
|
||||
esc_url( remove_query_arg( 'paged', $current_url ) ),
|
||||
'«'
|
||||
);
|
||||
|
||||
$page_links[] = sprintf( '<a class="%s" title="%s" href="%s">%s</a>',
|
||||
'prev-page' . $disable_first,
|
||||
esc_attr__( 'Go to the previous page' ),
|
||||
esc_url( add_query_arg( 'paged', max( 1, $current-1 ), $current_url ) ),
|
||||
'‹'
|
||||
);
|
||||
|
||||
if ( 'bottom' == $which )
|
||||
$html_current_page = $current;
|
||||
else
|
||||
$html_current_page = sprintf( '<input class="current-page" title="%s" type="text" name="%s" value="%s" size="%d" />',
|
||||
esc_attr__( 'Current page' ),
|
||||
esc_attr( 'paged' ),
|
||||
$current,
|
||||
strlen( $total_pages )
|
||||
);
|
||||
|
||||
$html_total_pages = sprintf( '<span class="total-pages">%s</span>', number_format_i18n( $total_pages ) );
|
||||
$page_links[] = '<span class="paging-input">' . sprintf( _x( '%1$s of %2$s', 'paging' ), $html_current_page, $html_total_pages ) . '</span>';
|
||||
|
||||
$page_links[] = sprintf( '<a class="%s" title="%s" href="%s">%s</a>',
|
||||
'next-page' . $disable_last,
|
||||
esc_attr__( 'Go to the next page' ),
|
||||
esc_url( add_query_arg( 'paged', min( $total_pages, $current+1 ), $current_url ) ),
|
||||
'›'
|
||||
);
|
||||
|
||||
$page_links[] = sprintf( '<a class="%s" title="%s" href="%s">%s</a>',
|
||||
'last-page' . $disable_last,
|
||||
esc_attr__( 'Go to the last page' ),
|
||||
esc_url( add_query_arg( 'paged', $total_pages, $current_url ) ),
|
||||
'»'
|
||||
);
|
||||
|
||||
$output .= "\n" . join( "\n", $page_links );
|
||||
|
||||
$page_class = $total_pages < 2 ? ' one-page' : '';
|
||||
|
||||
$this->_pagination = "<div class='tablenav-pages{$page_class}'>$output</div>";
|
||||
|
||||
if ( $echo ) {
|
||||
echo $this->_pagination;
|
||||
} else {
|
||||
return $this->_pagination;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
<?php
|
||||
/**
|
||||
* class-groups-user-capability.php
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.0.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* User Capability OPM.
|
||||
*/
|
||||
class Groups_User_Capability {
|
||||
|
||||
/**
|
||||
* Hook into appropriate actions.
|
||||
*
|
||||
* @see wp_delete_user()
|
||||
* @see remove_user_from_blog()
|
||||
*/
|
||||
public static function init() {
|
||||
|
||||
// when a user is deleted, user-capabilities must be removed
|
||||
// triggered by wp_delete_user()
|
||||
add_action( 'deleted_user', array( __CLASS__, 'deleted_user' ) );
|
||||
// when a capability is deleted the relationship must also be resolved
|
||||
add_action( 'groups_deleted_capability', array( __CLASS__, 'groups_deleted_capability' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist a user-capability relation.
|
||||
*
|
||||
* @param array $map attributes - must provide user_id and capability_id
|
||||
* @return true on success, otherwise false
|
||||
*/
|
||||
public static function create( $map ) {
|
||||
|
||||
global $wpdb;
|
||||
extract( $map );
|
||||
$result = false;
|
||||
|
||||
// avoid nonsense requests
|
||||
// if ( !empty( $user_id ) && !empty( $capability_id) ) {
|
||||
if ( !empty( $capability_id) ) {
|
||||
// make sure user and capability exist
|
||||
if ( ( false !== Groups_Utility::id( $user_id ) ) && get_user_by( 'id', $user_id ) && Groups_Capability::read( $capability_id ) ) {
|
||||
$user_capability_table = _groups_get_tablename( 'user_capability' );
|
||||
// don't try to create duplicate entries
|
||||
// also it would raise an error for duplicate PK
|
||||
if ( 0 === intval( $wpdb->get_var( $wpdb->prepare(
|
||||
"SELECT COUNT(*) FROM $user_capability_table WHERE user_id = %d AND capability_id = %d",
|
||||
Groups_Utility::id( $user_id ),
|
||||
Groups_Utility::id( $capability_id )
|
||||
) ) ) ) {
|
||||
$data = array(
|
||||
'user_id' => Groups_Utility::id( $user_id ),
|
||||
'capability_id' => Groups_Utility::id( $capability_id )
|
||||
);
|
||||
$formats = array( '%d', '%d' );
|
||||
if ( $wpdb->insert( $user_capability_table, $data, $formats ) ) {
|
||||
$result = true;
|
||||
do_action( 'groups_created_user_capability', $user_id, $capability_id );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a user-capability relation.
|
||||
*
|
||||
* @param int $user_id user's id
|
||||
* @param int $capability_id capability's id
|
||||
* @return object upon success, otherwise false
|
||||
*/
|
||||
public static function read( $user_id, $capability_id ) {
|
||||
global $wpdb;
|
||||
$result = false;
|
||||
|
||||
$user_capability_table = _groups_get_tablename( 'user_capability' );
|
||||
$user_capability = $wpdb->get_row( $wpdb->prepare(
|
||||
"SELECT * FROM $user_capability_table WHERE user_id = %d AND capability_id = %d",
|
||||
Groups_Utility::id( $user_id ),
|
||||
Groups_Utility::id( $capability_id )
|
||||
) );
|
||||
if ( $user_capability !== null ) {
|
||||
$result = $user_capability;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update user-capability relation.
|
||||
*
|
||||
* This changes nothing so as of now it's pointless to even call this.
|
||||
*
|
||||
* @param array $map
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
public static function update( $map ) {
|
||||
$result = false;
|
||||
// if ( !empty( $user_id ) && !empty( $capability_id) ) {
|
||||
if ( !empty( $capability_id) ) {
|
||||
// make sure user and capability exist
|
||||
if ( ( false !== Groups_Utility::id( $user_id ) ) && get_user_by( 'id', $user_id ) && Groups_Capability::read( $capability_id ) ) {
|
||||
$result = true;
|
||||
do_action( 'groups_updated_user_capability', $user_id, $capability_id );
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove user-capability relation.
|
||||
*
|
||||
* @param int $user_id
|
||||
* @param int $capability_id
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
public static function delete( $user_id, $capability_id ) {
|
||||
|
||||
global $wpdb;
|
||||
$result = false;
|
||||
|
||||
// avoid nonsense requests
|
||||
// if ( !empty( $user_id ) && !empty( $capability_id) ) {
|
||||
if ( !empty( $capability_id) ) {
|
||||
// to allow deletion of an entry after a user has been deleted,
|
||||
// we don't check if the user exists
|
||||
$user_capability_table = _groups_get_tablename( 'user_capability' );
|
||||
// get rid of it
|
||||
$rows = $wpdb->query( $wpdb->prepare(
|
||||
"DELETE FROM $user_capability_table WHERE user_id = %d AND capability_id = %d",
|
||||
Groups_Utility::id( $user_id ),
|
||||
Groups_Utility::id( $capability_id )
|
||||
) );
|
||||
// must have affected a row, otherwise no great success
|
||||
$result = ( $rows !== false ) && ( $rows > 0 );
|
||||
if ( $result ) {
|
||||
do_action( 'groups_deleted_user_capability', $user_id, $capability_id );
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooks into the deleted_user action to remove the deleted user from
|
||||
* all capabilities it is related to.
|
||||
*
|
||||
* @param int $user_id
|
||||
*/
|
||||
public static function deleted_user( $user_id ) {
|
||||
global $wpdb;
|
||||
|
||||
$user_capability_table = _groups_get_tablename( 'user_capability' );
|
||||
$rows = $wpdb->get_results( $wpdb->prepare(
|
||||
"SELECT * FROM $user_capability_table WHERE user_id = %d",
|
||||
Groups_Utility::id( $user_id )
|
||||
) );
|
||||
if ( $rows ) {
|
||||
foreach( $rows as $row ) {
|
||||
// don't optimize that in preference of a standard deletion
|
||||
// process (trigger actions ...)
|
||||
self::delete( $row->user_id, $row->capability_id );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooks into groups_deleted_capability to resolve all existing relations
|
||||
* between users and the deleted capability.
|
||||
* @param int $capability_id
|
||||
*/
|
||||
public static function groups_deleted_capability( $capability_id ) {
|
||||
global $wpdb;
|
||||
|
||||
$user_capability_table = _groups_get_tablename( 'user_capability' );
|
||||
$rows = $wpdb->get_results( $wpdb->prepare(
|
||||
"SELECT * FROM $user_capability_table WHERE capability_id = %d",
|
||||
Groups_Utility::id( $capability_id )
|
||||
) );
|
||||
if ( $rows ) {
|
||||
foreach( $rows as $row ) {
|
||||
// do NOT 'optimize' (must trigger actions ... same as above)
|
||||
self::delete( $row->user_id, $row->capability_id );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Groups_User_Capability::init();
|
||||
@@ -0,0 +1,282 @@
|
||||
<?php
|
||||
/**
|
||||
* class-groups-user-group.php
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.0.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* User Group OPM.
|
||||
*/
|
||||
class Groups_User_Group {
|
||||
|
||||
/**
|
||||
* @var persisted object
|
||||
*/
|
||||
var $user_group = null;
|
||||
|
||||
/**
|
||||
* Hook into appropriate actions.
|
||||
* @see wp_delete_user()
|
||||
* @see remove_user_from_blog()
|
||||
*/
|
||||
public static function init() {
|
||||
// when a user is deleted, it must be removed from all groups it
|
||||
// belongs to - triggered by wp_delete_user() and wpmu_delete_user()
|
||||
add_action( 'deleted_user', array( __CLASS__, 'deleted_user' ) );
|
||||
|
||||
// when a user is removed from a blog, the user must be removed
|
||||
// from all groups in that blog that it belongs to
|
||||
add_action( 'remove_user_from_blog', array( __CLASS__, 'remove_user_from_blog' ), 10, 2 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create by user and group id.
|
||||
* Must have been persisted.
|
||||
* @param int $user_id
|
||||
* @param int $group_id
|
||||
*/
|
||||
public function __construct( $user_id, $group_id ) {
|
||||
$this->user_group = self::read( $user_id, $group_id );
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a property by name.
|
||||
*
|
||||
* Possible properties:
|
||||
* - user_id
|
||||
* - group_id
|
||||
*
|
||||
* @param string $name property's name
|
||||
* @return property value, will return null if property does not exist
|
||||
*/
|
||||
public function __get( $name ) {
|
||||
$result = null;
|
||||
if ( $this->user_group !== null ) {
|
||||
switch( $name ) {
|
||||
case 'user_id' :
|
||||
case 'group_id' :
|
||||
$result = $this->user_group->$name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Persist a user-group relation.
|
||||
*
|
||||
* As of Groups 2.2.0, this is not invoked when entries for existing users are created on
|
||||
* plugin activation, thus the 'groups_created_user_group' action is not called for these.
|
||||
*
|
||||
* @param array $map attributes - must provide user_id and group_id
|
||||
* @return true on success, otherwise false
|
||||
*/
|
||||
public static function create( $map ) {
|
||||
|
||||
global $wpdb;
|
||||
extract( $map );
|
||||
$result = false;
|
||||
|
||||
// avoid nonsense requests
|
||||
if ( !empty( $group_id ) ) {
|
||||
// make sure user and group exist
|
||||
if (
|
||||
( false !== Groups_Utility::id( $user_id ) ) &&
|
||||
( $wpdb->get_var( $wpdb->prepare( "SELECT COUNT(ID) FROM $wpdb->users WHERE ID = %d", $user_id ) ) > 0 ) &&
|
||||
( $group = Groups_Group::read( $group_id ) )
|
||||
) {
|
||||
// only allow to add users to groups if they belong to the
|
||||
// group's blog or we have the anonymous user
|
||||
if ( is_user_member_of_blog( Groups_Utility::id( $user_id ) ) || ( Groups_Utility::id( $user_id ) === 0 ) ) {
|
||||
$user_group_table = _groups_get_tablename( 'user_group' );
|
||||
// don't try to create duplicate entries
|
||||
// also it would raise an error for duplicate PK
|
||||
if ( 0 === intval( $wpdb->get_var( $wpdb->prepare(
|
||||
"SELECT COUNT(*) FROM $user_group_table WHERE user_id = %d AND group_id = %d",
|
||||
Groups_Utility::id( $user_id ),
|
||||
Groups_Utility::id( $group_id ) ) ) )
|
||||
) {
|
||||
$data = array(
|
||||
'user_id' => Groups_Utility::id( $user_id ),
|
||||
'group_id' => Groups_Utility::id( $group_id )
|
||||
);
|
||||
$formats = array( '%d', '%d' );
|
||||
if ( $wpdb->insert( $user_group_table, $data, $formats ) ) {
|
||||
$result = true;
|
||||
do_action( 'groups_created_user_group', $user_id, $group_id );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a user-group relation.
|
||||
*
|
||||
* @param int $user_id user's id
|
||||
* @param int $group_id group's id
|
||||
* @return object upon success, otherwise false
|
||||
*/
|
||||
public static function read( $user_id, $group_id ) {
|
||||
global $wpdb;
|
||||
$result = false;
|
||||
|
||||
$user_group_table = _groups_get_tablename( 'user_group' );
|
||||
$user_group = $wpdb->get_row( $wpdb->prepare(
|
||||
"SELECT * FROM $user_group_table WHERE user_id = %d AND group_id = %d",
|
||||
Groups_Utility::id( $user_id ),
|
||||
Groups_Utility::id( $group_id )
|
||||
) );
|
||||
if ( $user_group !== null ) {
|
||||
$result = $user_group;
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update user-group relation.
|
||||
*
|
||||
* This is a relation and as the relation is, this does nothing and
|
||||
* it SHOULD do nothing.
|
||||
*
|
||||
* @param array $map
|
||||
* @return true on success, otherwise false
|
||||
*/
|
||||
public static function update( $map ) {
|
||||
$result = false;
|
||||
// if ( !empty( $user_id ) && !empty( $group_id) ) {
|
||||
if ( !empty( $group_id) ) {
|
||||
// make sure user and group exist
|
||||
if ( ( false !== Groups_Utility::id( $user_id ) ) && get_user_by( 'id', $user_id ) && Groups_Group::read( $group_id ) ) {
|
||||
$result = true;
|
||||
do_action( 'groups_updated_user_group', $user_id, $group_id );
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove user-group relation.
|
||||
*
|
||||
* @param int $user_id
|
||||
* @param int $group_id
|
||||
* @return true if successful, false otherwise
|
||||
*/
|
||||
public static function delete( $user_id, $group_id ) {
|
||||
|
||||
global $wpdb;
|
||||
$result = false;
|
||||
|
||||
// avoid nonsense requests
|
||||
// if ( !empty( $user_id ) && !empty( $group_id ) ) {
|
||||
if ( !empty( $group_id ) ) {
|
||||
// to allow deletion of an entry after a user has been deleted,
|
||||
// we don't check if the user exists
|
||||
$user_group_table = _groups_get_tablename( 'user_group' );
|
||||
// get rid of it
|
||||
$rows = $wpdb->query( $wpdb->prepare(
|
||||
"DELETE FROM $user_group_table WHERE user_id = %d AND group_id = %d",
|
||||
Groups_Utility::id( $user_id ),
|
||||
Groups_Utility::id( $group_id )
|
||||
) );
|
||||
// must have affected a row, otherwise no great success
|
||||
$result = ( $rows !== false ) && ( $rows > 0 );
|
||||
if ( $result ) {
|
||||
do_action( 'groups_deleted_user_group', $user_id, $group_id );
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooks into the deleted_user action to remove the deleted user from
|
||||
* all groups it belongs to.
|
||||
*
|
||||
* @param int $user_id
|
||||
*/
|
||||
public static function deleted_user( $user_id ) {
|
||||
global $wpdb;
|
||||
|
||||
$user_group_table = _groups_get_tablename( 'user_group' );
|
||||
$rows = $wpdb->get_results( $wpdb->prepare(
|
||||
"SELECT * FROM $user_group_table WHERE user_id = %d",
|
||||
Groups_Utility::id( $user_id )
|
||||
) );
|
||||
if ( $rows ) {
|
||||
foreach( $rows as $row ) {
|
||||
// don't optimize that in preference of a standard deletion
|
||||
// process (trigger actions ...)
|
||||
self::delete( $row->user_id, $row->group_id );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Hooks into the remove_user_from_blog action to remove the user
|
||||
* from groups that belong to that blog.
|
||||
*
|
||||
* Note that this is preemptive as there is no
|
||||
* removed_user_from_blog action.
|
||||
*
|
||||
* @param int $user_id
|
||||
* @param int $blog_id
|
||||
*/
|
||||
public static function remove_user_from_blog( $user_id, $blog_id ) {
|
||||
|
||||
if ( is_multisite() ) {
|
||||
Groups_Controller::switch_to_blog( $blog_id );
|
||||
}
|
||||
|
||||
global $wpdb;
|
||||
|
||||
$group_table = _groups_get_tablename( 'group' );
|
||||
$user_group_table = _groups_get_tablename( 'user_group' );
|
||||
// We can end up here while a blog is being deleted, in that case,
|
||||
// the tables have already been deleted.
|
||||
if ( ( $wpdb->get_var( "SHOW TABLES LIKE '" . $group_table . "'" ) == $group_table ) &&
|
||||
( $wpdb->get_var( "SHOW TABLES LIKE '" . $user_group_table . "'" ) == $user_group_table )
|
||||
) {
|
||||
|
||||
$rows = $wpdb->get_results( $wpdb->prepare(
|
||||
"SELECT * FROM $user_group_table
|
||||
LEFT JOIN $group_table ON $user_group_table.group_id = $group_table.group_id
|
||||
WHERE $user_group_table.user_id = %d
|
||||
",
|
||||
Groups_Utility::id( $user_id )
|
||||
) );
|
||||
if ( $rows ) {
|
||||
foreach( $rows as $row ) {
|
||||
// don't optimize that, favour standard deletion
|
||||
self::delete( $row->user_id, $row->group_id );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if ( is_multisite() ) {
|
||||
Groups_Controller::restore_current_blog();
|
||||
}
|
||||
}
|
||||
}
|
||||
Groups_User_Group::init();
|
||||
@@ -0,0 +1,460 @@
|
||||
<?php
|
||||
/**
|
||||
* class-groups-user.php
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.0.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
require_once( GROUPS_CORE_LIB . '/interface-i-capable.php' );
|
||||
require_once( GROUPS_CORE_LIB . '/class-groups-capability.php' );
|
||||
|
||||
/**
|
||||
* User OPM.
|
||||
*/
|
||||
class Groups_User implements I_Capable {
|
||||
|
||||
const CACHE_GROUP = 'groups';
|
||||
const CAPABILITIES = 'capabilities';
|
||||
const CAPABILITIES_BASE = 'capabilities_base';
|
||||
const CAPABILITY_IDS = 'capability_ids';
|
||||
const CAPABILITY_IDS_BASE = 'capability_ids_base';
|
||||
const GROUP_IDS = 'group_ids';
|
||||
const GROUP_IDS_BASE = 'group_ids_base';
|
||||
const GROUPS = 'groups';
|
||||
const GROUPS_BASE = 'groups_base';
|
||||
|
||||
/**
|
||||
* User object.
|
||||
*
|
||||
* @var WP_User
|
||||
*/
|
||||
var $user = null;
|
||||
|
||||
/**
|
||||
* Hook cache clearers to actions that can modify the capabilities.
|
||||
*/
|
||||
public static function init() {
|
||||
add_action( 'groups_created_user_group', array( __CLASS__, 'clear_cache' ) );
|
||||
add_action( 'groups_updated_user_group', array( __CLASS__, 'clear_cache' ) );
|
||||
add_action( 'groups_deleted_user_group', array( __CLASS__, 'clear_cache' ) );
|
||||
add_action( 'groups_created_user_capability', array( __CLASS__, 'clear_cache' ) );
|
||||
add_action( 'groups_updated_user_capability', array( __CLASS__, 'clear_cache' ) );
|
||||
add_action( 'groups_deleted_user_capability', array( __CLASS__, 'clear_cache' ) );
|
||||
add_action( 'groups_created_group_capability', array( __CLASS__, 'clear_cache_for_group' ) );
|
||||
add_action( 'groups_updated_group_capability', array( __CLASS__, 'clear_cache_for_group' ) );
|
||||
add_action( 'groups_deleted_group_capability', array( __CLASS__, 'clear_cache_for_group' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear cache objects for the user.
|
||||
* @param int $user_id
|
||||
*/
|
||||
public static function clear_cache( $user_id ) {
|
||||
// be lazy, clear the entries so they are rebuilt when requested
|
||||
Groups_Cache::delete( self::CAPABILITIES . $user_id, self::CACHE_GROUP );
|
||||
Groups_Cache::delete( self::CAPABILITIES_BASE . $user_id, self::CACHE_GROUP );
|
||||
Groups_Cache::delete( self::CAPABILITY_IDS . $user_id, self::CACHE_GROUP );
|
||||
Groups_Cache::delete( self::CAPABILITY_IDS_BASE . $user_id, self::CACHE_GROUP );
|
||||
Groups_Cache::delete( self::GROUP_IDS . $user_id, self::CACHE_GROUP );
|
||||
Groups_Cache::delete( self::GROUP_IDS_BASE . $user_id, self::CACHE_GROUP );
|
||||
Groups_Cache::delete( self::GROUPS . $user_id, self::CACHE_GROUP );
|
||||
Groups_Cache::delete( self::GROUPS_BASE . $user_id, self::CACHE_GROUP );
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear cache objects for all users in the group.
|
||||
* @param unknown_type $group_id
|
||||
*/
|
||||
public static function clear_cache_for_group( $group_id ) {
|
||||
global $wpdb;
|
||||
if ( $group = Groups_Group::read( $group_id ) ) {
|
||||
// not using $group->users, as we don't need a lot of user objects created here
|
||||
$user_group_table = _groups_get_tablename( 'user_group' );
|
||||
$users = $wpdb->get_results( $wpdb->prepare(
|
||||
"SELECT ID FROM $wpdb->users LEFT JOIN $user_group_table ON $wpdb->users.ID = $user_group_table.user_id WHERE $user_group_table.group_id = %d",
|
||||
Groups_Utility::id( $group_id )
|
||||
) );
|
||||
if ( $users ) {
|
||||
foreach( $users as $user ) {
|
||||
self::clear_cache( $user->ID );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create, if $user_id = 0 an anonymous user is assumed.
|
||||
*
|
||||
* @param int $user_id
|
||||
*/
|
||||
public function __construct( $user_id = null ) {
|
||||
if ( $user_id !== null ) {
|
||||
if ( Groups_Utility::id( $user_id ) ) {
|
||||
$this->user = get_user_by( 'id', $user_id );
|
||||
if ( !$this->user ) {
|
||||
$this->user = new WP_User( 0 );
|
||||
}
|
||||
} else {
|
||||
$this->user = new WP_User( 0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve a user property.
|
||||
* Must be "capabilities", "groups" or a property of the WP_User class.
|
||||
* @param string $name property's name
|
||||
*/
|
||||
public function __get( $name ) {
|
||||
|
||||
global $wpdb;
|
||||
$result = null;
|
||||
|
||||
if ( $this->user !== null ) {
|
||||
|
||||
switch ( $name ) {
|
||||
|
||||
case 'capability_ids' :
|
||||
$cached = Groups_Cache::get( self::CAPABILITY_IDS_BASE . $this->user->ID, self::CACHE_GROUP );
|
||||
if ( $cached !== null ) {
|
||||
$result = $cached->value;
|
||||
unset( $cached );
|
||||
} else {
|
||||
$user_capability_table = _groups_get_tablename( 'user_capability' );
|
||||
$rows = $wpdb->get_results( $wpdb->prepare(
|
||||
"SELECT capability_id FROM $user_capability_table WHERE user_id = %d",
|
||||
Groups_Utility::id( $this->user->ID )
|
||||
) );
|
||||
if ( $rows ) {
|
||||
$result = array();
|
||||
foreach ( $rows as $row ) {
|
||||
$result[] = $row->capability_id;
|
||||
}
|
||||
}
|
||||
Groups_Cache::set( self::CAPABILITY_IDS_BASE . $this->user->ID, $result, self::CACHE_GROUP );
|
||||
}
|
||||
break;
|
||||
|
||||
case 'capability_ids_deep' :
|
||||
if ( $this->user !== null ) {
|
||||
$cached = Groups_Cache::get( self::CAPABILITY_IDS . $this->user->ID, self::CACHE_GROUP );
|
||||
if ( $cached !== null ) {
|
||||
$capability_ids = $cached->value;
|
||||
unset( $cached );
|
||||
} else {
|
||||
$this->init_cache( $capability_ids );
|
||||
}
|
||||
$result = $capability_ids;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'group_ids' :
|
||||
$cached = Groups_Cache::get( self::GROUP_IDS_BASE . $this->user->ID, self::CACHE_GROUP );
|
||||
if ( $cached !== null ) {
|
||||
$result = $cached->value;
|
||||
unset( $cached );
|
||||
} else {
|
||||
$user_group_table = _groups_get_tablename( 'user_group' );
|
||||
$rows = $wpdb->get_results( $wpdb->prepare(
|
||||
"SELECT group_id FROM $user_group_table WHERE user_id = %d",
|
||||
Groups_Utility::id( $this->user->ID )
|
||||
) );
|
||||
if ( $rows ) {
|
||||
$result = array();
|
||||
foreach( $rows as $row ) {
|
||||
$result[] = $row->group_id;
|
||||
}
|
||||
}
|
||||
Groups_Cache::set( self::GROUP_IDS_BASE . $this->user->ID, $result, self::CACHE_GROUP );
|
||||
}
|
||||
break;
|
||||
|
||||
case 'group_ids_deep' :
|
||||
if ( $this->user !== null ) {
|
||||
$cached = Groups_Cache::get( self::GROUP_IDS . $this->user->ID, self::CACHE_GROUP );
|
||||
if ( $cached !== null ) {
|
||||
$group_ids = $cached->value;
|
||||
unset( $cached );
|
||||
} else {
|
||||
$this->init_cache( $capability_ids, $capabilities, $group_ids );
|
||||
}
|
||||
$result = $group_ids;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'capabilities' :
|
||||
$cached = Groups_Cache::get( self::CAPABILITIES_BASE . $this->user->ID, self::CACHE_GROUP );
|
||||
if ( $cached !== null ) {
|
||||
$result = $cached->value;
|
||||
unset( $cached );
|
||||
} else {
|
||||
$user_capability_table = _groups_get_tablename( 'user_capability' );
|
||||
$rows = $wpdb->get_results( $wpdb->prepare(
|
||||
"SELECT capability_id FROM $user_capability_table WHERE user_id = %d",
|
||||
Groups_Utility::id( $this->user->ID )
|
||||
) );
|
||||
if ( $rows ) {
|
||||
$result = array();
|
||||
foreach ( $rows as $row ) {
|
||||
$result[] = new Groups_Capability( $row->capability_id );
|
||||
}
|
||||
}
|
||||
Groups_Cache::set( self::CAPABILITIES_BASE . $this->user->ID, $result, self::CACHE_GROUP );
|
||||
}
|
||||
break;
|
||||
|
||||
case 'capabilities_deep' :
|
||||
if ( $this->user !== null ) {
|
||||
$cached = Groups_Cache::get( self::CAPABILITIES . $this->user->ID, self::CACHE_GROUP );
|
||||
if ( $cached !== null ) {
|
||||
$capabilities = $cached->value;
|
||||
unset( $cached );
|
||||
} else {
|
||||
$this->init_cache( $capability_ids, $capabilities );
|
||||
}
|
||||
$result = $capabilities;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'groups' :
|
||||
$cached = Groups_Cache::get( self::GROUPS_BASE . $this->user->ID, self::CACHE_GROUP );
|
||||
if ( $cached !== null ) {
|
||||
$result = $cached->value;
|
||||
unset( $cached );
|
||||
} else {
|
||||
$user_group_table = _groups_get_tablename( 'user_group' );
|
||||
$rows = $wpdb->get_results( $wpdb->prepare(
|
||||
"SELECT group_id FROM $user_group_table WHERE user_id = %d",
|
||||
Groups_Utility::id( $this->user->ID )
|
||||
) );
|
||||
if ( $rows ) {
|
||||
$result = array();
|
||||
foreach( $rows as $row ) {
|
||||
$result[] = new Groups_Group( $row->group_id );
|
||||
}
|
||||
}
|
||||
Groups_Cache::set( self::GROUPS_BASE . $this->user->ID, $result, self::CACHE_GROUP );
|
||||
}
|
||||
break;
|
||||
|
||||
case 'groups_deep' :
|
||||
$cached = Groups_Cache::get( self::GROUPS . $this->user->ID, self::CACHE_GROUP );
|
||||
if ( $cached !== null ) {
|
||||
$result = $cached->value;
|
||||
unset( $cached );
|
||||
} else {
|
||||
$result = array();
|
||||
foreach( $this->group_ids_deep as $group_id ) {
|
||||
$result[] = new Groups_Group( $group_id );
|
||||
}
|
||||
Groups_Cache::set( self::GROUPS . $this->user->ID, $result, self::CACHE_GROUP );
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
$result = $this->user->$name;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* (non-PHPdoc)
|
||||
* @see I_Capable::can()
|
||||
*/
|
||||
public function can( $capability ) {
|
||||
|
||||
global $wpdb;
|
||||
$result = false;
|
||||
|
||||
if ( $this->user !== null ) {
|
||||
if ( _groups_admin_override( $this->user->ID ) ) {
|
||||
$result = true;
|
||||
} else {
|
||||
// determine capability id
|
||||
$capability_id = null;
|
||||
if ( is_numeric( $capability ) ) {
|
||||
$capability_id = Groups_Utility::id( $capability );
|
||||
$cached = Groups_Cache::get( self::CAPABILITY_IDS . $this->user->ID, self::CACHE_GROUP );
|
||||
if ( $cached !== null ) {
|
||||
$capability_ids = $cached->value;
|
||||
unset( $cached );
|
||||
} else {
|
||||
$this->init_cache( $capability_ids );
|
||||
}
|
||||
$result = in_array( $capability_id, $capability_ids );
|
||||
} else if ( is_string( $capability ) ) {
|
||||
$cached = Groups_Cache::get( self::CAPABILITIES . $this->user->ID, self::CACHE_GROUP );
|
||||
if ( $cached !== null ) {
|
||||
$capabilities = $cached->value;
|
||||
unset( $cached );
|
||||
} else {
|
||||
$this->init_cache( $capability_ids, $capabilities );
|
||||
}
|
||||
$result = in_array( $capability, $capabilities );
|
||||
}
|
||||
}
|
||||
}
|
||||
$result = apply_filters_ref_array( 'groups_user_can', array( $result, &$this, $capability ) );
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the user belongs to the group.
|
||||
*
|
||||
* @param int $group_id
|
||||
* @return boolean
|
||||
*/
|
||||
public function is_member( $group_id ) {
|
||||
$result = false;
|
||||
if ( $this->user !== null ) {
|
||||
if ( isset( $this->user->ID ) ) {
|
||||
$user_group = Groups_User_Group::read(
|
||||
Groups_Utility::id( $this->user->ID ),
|
||||
Groups_Utility::id( $group_id )
|
||||
);
|
||||
$result = $user_group !== false;
|
||||
unset( $user_group );
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds the cache entries for user groups and capabilities if needed.
|
||||
* The cache entries are built only if they do not already exist.
|
||||
* If you want them rebuilt, delete them before calling.
|
||||
*
|
||||
* @param array $capability_ids carries the capability ids for the user on return, but only if cache entries have been built; will provide an empty array by default
|
||||
* @param array $capabilities carries the capabilities for the user on return, but only if cache entries have been built; will provide an empty array by default
|
||||
* @param array $group_ids carries the group ids for the user on return, but only if cache entries have been built; will provide an empty array by default
|
||||
*/
|
||||
private function init_cache( &$capability_ids = null, &$capabilities = null, &$group_ids = null ) {
|
||||
|
||||
global $wpdb;
|
||||
|
||||
$capabilities = array();
|
||||
$capability_ids = array();
|
||||
$group_ids = array();
|
||||
|
||||
if ( ( $this->user !== null ) && ( Groups_Cache::get( self::GROUP_IDS . $this->user->ID, self::CACHE_GROUP ) === null ) ) {
|
||||
$group_table = _groups_get_tablename( 'group' );
|
||||
$capability_table = _groups_get_tablename( 'capability' );
|
||||
$group_capability_table = _groups_get_tablename( 'group_capability' );
|
||||
$user_group_table = _groups_get_tablename( 'user_group' );
|
||||
$user_capability_table = _groups_get_tablename( 'user_capability' );
|
||||
|
||||
$limit = $wpdb->get_var( "SELECT COUNT(*) FROM $group_table" );
|
||||
if ( $limit === null ) {
|
||||
$limit = 1;
|
||||
}
|
||||
|
||||
// note that limits by blog_id for multisite are
|
||||
// enforced when a user is added to a blog
|
||||
$user_groups = $wpdb->get_results( $wpdb->prepare(
|
||||
"SELECT group_id FROM $user_group_table WHERE user_id = %d",
|
||||
Groups_Utility::id( $this->user->ID )
|
||||
) );
|
||||
// get all capabilities directly assigned (those granted through
|
||||
// groups are added below
|
||||
$user_capabilities = $wpdb->get_results( $wpdb->prepare(
|
||||
"SELECT c.capability_id, c.capability FROM $user_capability_table uc LEFT JOIN $capability_table c ON c.capability_id = uc.capability_id WHERE user_id = %d",
|
||||
Groups_Utility::id( $this->user->ID )
|
||||
) );
|
||||
if ( $user_capabilities ) {
|
||||
foreach( $user_capabilities as $user_capability ) {
|
||||
$capabilities[] = $user_capability->capability;
|
||||
$capability_ids[] = $user_capability->capability_id;
|
||||
}
|
||||
}
|
||||
|
||||
if ( apply_filters( 'groups_user_add_role_capabilities', true ) ) {
|
||||
// Get all capabilities from the WP_User object.
|
||||
$role_caps = $this->user->get_role_caps();
|
||||
if ( !empty( $role_caps ) && is_array( $role_caps ) ) {
|
||||
$caps = array();
|
||||
foreach( $role_caps as $role_cap => $has ) {
|
||||
if ( $has && !in_array( $role_cap, $capabilities ) ) {
|
||||
$caps[] = "'" . $role_cap . "'";
|
||||
}
|
||||
}
|
||||
if ( !empty( $caps ) ) {
|
||||
// Retrieve the capabilities and only add those that are
|
||||
// recognized. Note that this also effectively filters out
|
||||
// all roles and that this is desired.
|
||||
if ( $role_capabilities = $wpdb->get_results( "SELECT capability_id, capability FROM $capability_table c WHERE capability IN (" . implode( ',', $caps ) . ")" ) ) {
|
||||
foreach( $role_capabilities as $role_capability ) {
|
||||
$capabilities[] = $role_capability->capability;
|
||||
$capability_ids[] = $role_capability->capability_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get all groups the user belongs to directly or through
|
||||
// inheritance along with their capabilities.
|
||||
if ( $user_groups ) {
|
||||
foreach( $user_groups as $user_group ) {
|
||||
$group_ids[] = Groups_Utility::id( $user_group->group_id );
|
||||
}
|
||||
if ( count( $group_ids ) > 0 ) {
|
||||
$iterations = 0;
|
||||
$old_group_ids_count = 0;
|
||||
while( ( $iterations < $limit ) && ( count( $group_ids ) !== $old_group_ids_count ) ) {
|
||||
$iterations++;
|
||||
$old_group_ids_count = count( $group_ids );
|
||||
$id_list = implode( ',', $group_ids );
|
||||
$parent_group_ids = $wpdb->get_results(
|
||||
"SELECT parent_id FROM $group_table WHERE parent_id IS NOT NULL AND group_id IN ($id_list)"
|
||||
);
|
||||
if ( $parent_group_ids ) {
|
||||
foreach( $parent_group_ids as $parent_group_id ) {
|
||||
$parent_group_id = Groups_Utility::id( $parent_group_id->parent_id );
|
||||
if ( !in_array( $parent_group_id, $group_ids ) ) {
|
||||
$group_ids[] = $parent_group_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$id_list = implode( ',', $group_ids );
|
||||
$rows = $wpdb->get_results(
|
||||
"SELECT $group_capability_table.capability_id, $capability_table.capability FROM $group_capability_table LEFT JOIN $capability_table ON $group_capability_table.capability_id = $capability_table.capability_id WHERE group_id IN ($id_list)"
|
||||
);
|
||||
if ( count( $rows ) > 0 ) {
|
||||
foreach ( $rows as $row ) {
|
||||
if ( !in_array( $row->capability_id, $capability_ids ) ) {
|
||||
$capabilities[] = $row->capability;
|
||||
$capability_ids[] = $row->capability_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Groups_Cache::set( self::CAPABILITIES . $this->user->ID, $capabilities, self::CACHE_GROUP );
|
||||
Groups_Cache::set( self::CAPABILITY_IDS . $this->user->ID, $capability_ids, self::CACHE_GROUP );
|
||||
Groups_Cache::set( self::GROUP_IDS . $this->user->ID, $group_ids, self::CACHE_GROUP );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Groups_User::init();
|
||||
@@ -0,0 +1,137 @@
|
||||
<?php
|
||||
/**
|
||||
* class-groups-utility.php
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.0.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility functions.
|
||||
*/
|
||||
class Groups_Utility {
|
||||
|
||||
/**
|
||||
* Checks an id (0 is accepted => anonymous).
|
||||
*
|
||||
* @param string|int $id
|
||||
* @return int|boolean if validated, the id as an int, otherwise false
|
||||
*/
|
||||
public static function id( $id ) {
|
||||
$result = false;
|
||||
if ( is_numeric( $id ) ) {
|
||||
$id = intval( $id );
|
||||
//if ( $id > 0 ) {
|
||||
if ( $id >= 0 ) { // 0 => anonymous
|
||||
$result = $id;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of blog_ids for current blogs.
|
||||
* @return array of int with blog ids
|
||||
*/
|
||||
public static function get_blogs() {
|
||||
global $wpdb;
|
||||
$result = array();
|
||||
if ( is_multisite() ) {
|
||||
$blogs = $wpdb->get_results( $wpdb->prepare(
|
||||
"SELECT blog_id FROM $wpdb->blogs WHERE site_id = %d AND archived = '0' AND spam = '0' AND deleted = '0' ORDER BY registered DESC",
|
||||
$wpdb->siteid
|
||||
) );
|
||||
if ( is_array( $blogs ) ) {
|
||||
foreach( $blogs as $blog ) {
|
||||
$result[] = $blog->blog_id;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$result[] = get_current_blog_id();
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
|
||||
public static function get_group_tree( &$tree = null ) {
|
||||
global $wpdb;
|
||||
$group_table = _groups_get_tablename( 'group' );
|
||||
|
||||
if ( $tree === null ) {
|
||||
$tree = array();
|
||||
$root_groups = $wpdb->get_results( "SELECT group_id FROM $group_table WHERE parent_id IS NULL" );
|
||||
if ( $root_groups ) {
|
||||
foreach( $root_groups as $root_group ) {
|
||||
$group_id = Groups_Utility::id( $root_group->group_id );
|
||||
$tree[$group_id] = array();
|
||||
}
|
||||
}
|
||||
self::get_group_tree( $tree );
|
||||
} else {
|
||||
foreach( $tree as $group_id => $nodes ) {
|
||||
$children = $wpdb->get_results( $wpdb->prepare(
|
||||
"SELECT group_id FROM $group_table WHERE parent_id = %d",
|
||||
Groups_Utility::id( $group_id )
|
||||
) );
|
||||
foreach( $children as $child ) {
|
||||
$tree[$group_id][$child->group_id] = array();
|
||||
}
|
||||
self::get_group_tree( $tree[$group_id] );
|
||||
}
|
||||
}
|
||||
|
||||
return $tree;
|
||||
}
|
||||
|
||||
public static function render_group_tree( &$tree, &$output ) {
|
||||
$output .= '<ul style="padding-left:1em">';
|
||||
foreach( $tree as $group_id => $nodes ) {
|
||||
$output .= '<li>';
|
||||
$group = Groups_Group::read( $group_id );
|
||||
if ( $group ) {
|
||||
$output .= $group->name;
|
||||
}
|
||||
if ( !empty( $nodes ) ) {
|
||||
self::render_group_tree( $nodes, $output );
|
||||
}
|
||||
$output .= '</li>';
|
||||
}
|
||||
$output .= '</ul>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the two object's names, used for groups and
|
||||
* capabilities, i.e. Groups_Group and Groups_Capability can be compared
|
||||
* if both are of the same class. Otherwise this will return 0.
|
||||
*
|
||||
* @param Groups_Group|Groups_Capability $o1
|
||||
* @param Groups_Group|Groups_Capability $o2 must match the class of $o1
|
||||
* @return number
|
||||
*/
|
||||
public static function cmp( $o1, $o2 ) {
|
||||
$result = 0;
|
||||
if ( $o1 instanceof Groups_Group && $o2 instanceof Groups_Group ) {
|
||||
$result = strcmp( $o1->name, $o2->name );
|
||||
} else if ( $o1 instanceof Groups_Capability && $o2 instanceof Groups_Capability ) {
|
||||
$result = strcmp( $o1->capability->capability, $o2->capability->capability );
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
/**
|
||||
* constants.php
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.0.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* @var string plugin version dummy
|
||||
*/
|
||||
define( 'GROUPS_DEFAULT_VERSION', '1.0.0' );
|
||||
|
||||
/**
|
||||
* Do NOT remove this constant.
|
||||
*
|
||||
* @var string plugin domain
|
||||
*/
|
||||
define( 'GROUPS_PLUGIN_DOMAIN', 'groups' );
|
||||
|
||||
/**
|
||||
* @var string plugin directory on the server
|
||||
*/
|
||||
define( 'GROUPS_PLUGIN_DIR', GROUPS_CORE_DIR );
|
||||
|
||||
/**
|
||||
* @var string plugin url
|
||||
*/
|
||||
define( 'GROUPS_PLUGIN_URL', trailingslashit( GROUPS_CORE_URL ) );
|
||||
|
||||
/**
|
||||
* @var string groups table prefix
|
||||
*/
|
||||
define( 'GROUPS_TP', 'groups_' );
|
||||
|
||||
// administrative capabilities
|
||||
|
||||
/**
|
||||
* @var string grants access to the groups section
|
||||
*/
|
||||
define( 'GROUPS_ACCESS_GROUPS', 'groups_access' );
|
||||
|
||||
/**
|
||||
* @var string grants CRUD for groups (CRUD)
|
||||
*/
|
||||
define( 'GROUPS_ADMINISTER_GROUPS', 'groups_admin_groups');
|
||||
|
||||
/**
|
||||
* @var string grants to administer plugin options
|
||||
*/
|
||||
define( 'GROUPS_ADMINISTER_OPTIONS', 'groups_admin_options');
|
||||
|
||||
/**
|
||||
* @var string grants permission to restrict access on posts etc.
|
||||
*/
|
||||
define( 'GROUPS_RESTRICT_ACCESS', 'groups_restrict_access' );
|
||||
|
||||
/**
|
||||
* @var string admin nonce
|
||||
*/
|
||||
define( 'GROUPS_ADMIN_GROUPS_NONCE', 'groups-admin-nonce' );
|
||||
|
||||
/**
|
||||
* @var string admin override option
|
||||
* @deprecated since 2.1.1
|
||||
*/
|
||||
define( 'GROUPS_ADMINISTRATOR_ACCESS_OVERRIDE', 'groups-admin-override' );
|
||||
|
||||
/**
|
||||
* @var string admin override option default setting
|
||||
* @deprecated since 2.1.1
|
||||
*/
|
||||
define( 'GROUPS_ADMINISTRATOR_ACCESS_OVERRIDE_DEFAULT', false );
|
||||
|
||||
/**
|
||||
* @var string read post capabilities option
|
||||
*/
|
||||
define( 'GROUPS_READ_POST_CAPABILITIES', 'groups-read-post-capabilities' );
|
||||
|
||||
/**
|
||||
* Tree view option
|
||||
* @var string
|
||||
*/
|
||||
define( 'GROUPS_SHOW_TREE_VIEW', 'groups-show-tree-view' );
|
||||
|
||||
/**
|
||||
* Tree view option default.
|
||||
* @var boolean
|
||||
*/
|
||||
define( 'GROUPS_SHOW_TREE_VIEW_DEFAULT', false );
|
||||
|
||||
/**
|
||||
* Option to show groups info in the user profile.
|
||||
* @var string
|
||||
*/
|
||||
define( 'GROUPS_SHOW_IN_USER_PROFILE', 'groups-show-in-user-profile' );
|
||||
|
||||
/**
|
||||
* Default for showing groups in user profiles.
|
||||
* @var boolean
|
||||
*/
|
||||
define( 'GROUPS_SHOW_IN_USER_PROFILE_DEFAULT', false );
|
||||
|
||||
/**
|
||||
* Whether legacy functions should be supported.
|
||||
* @var string
|
||||
*/
|
||||
define( 'GROUPS_LEGACY_ENABLE', 'groups-legacy-enable' );
|
||||
|
||||
/**
|
||||
* Default value for legacy support.
|
||||
* @var boolean
|
||||
*/
|
||||
define( 'GROUPS_LEGACY_ENABLE_DEFAULT', false );
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
/**
|
||||
* interface-i-capable.php
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.0.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Capable interface OPM.
|
||||
*/
|
||||
interface I_Capable {
|
||||
|
||||
/**
|
||||
* Finds out if I have the given capability.
|
||||
*
|
||||
* @param string|int $capability capability or capability id
|
||||
* @return true if I can, otherwise false
|
||||
*/
|
||||
public function can( $capability );
|
||||
}
|
||||
172
backend/wordpress/wp-content/plugins/groups/lib/core/wp-init.php
Normal file
@@ -0,0 +1,172 @@
|
||||
<?php
|
||||
/**
|
||||
* wp-init.php
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.0.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// startup
|
||||
global $groups_version, $groups_admin_messages;
|
||||
|
||||
if ( !isset( $groups_admin_messages ) ) {
|
||||
$groups_admin_messages = array();
|
||||
}
|
||||
|
||||
if ( !isset( $groups_version ) ) {
|
||||
$groups_version = GROUPS_CORE_VERSION;
|
||||
}
|
||||
|
||||
// <= 3.2.1
|
||||
if ( !function_exists( 'is_user_member_of_blog' ) ) {
|
||||
function is_user_member_of_blog( $user_id, $blog_id = 0 ) {
|
||||
return false !== get_user_by( 'id', $user_id );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load core :
|
||||
*/
|
||||
|
||||
require_once GROUPS_CORE_LIB . '/class-groups-cache.php';
|
||||
require_once GROUPS_CORE_LIB . '/class-groups-cache-object.php';
|
||||
|
||||
require_once( GROUPS_CORE_LIB . '/class-groups-utility.php' );
|
||||
|
||||
// options
|
||||
require_once( GROUPS_CORE_LIB . '/class-groups-options.php' );
|
||||
|
||||
// plugin control: activation, deactivation, ...
|
||||
require_once( GROUPS_CORE_LIB . '/class-groups-controller.php' );
|
||||
|
||||
// legacy enabled?
|
||||
$groups_legacy_enable = Groups_Options::get_option( GROUPS_LEGACY_ENABLE, GROUPS_LEGACY_ENABLE_DEFAULT );
|
||||
|
||||
// admin
|
||||
if ( is_admin() ) {
|
||||
require_once( GROUPS_ADMIN_LIB . '/class-groups-admin.php' );
|
||||
require_once( GROUPS_ADMIN_LIB . '/class-groups-admin-welcome.php' );
|
||||
if ( Groups_Options::get_option( GROUPS_SHOW_IN_USER_PROFILE, GROUPS_SHOW_IN_USER_PROFILE_DEFAULT ) ) {
|
||||
require_once( GROUPS_ADMIN_LIB . '/class-groups-admin-user-profile.php' );
|
||||
}
|
||||
require_once( GROUPS_ADMIN_LIB . '/class-groups-admin-users.php' );
|
||||
require_once( GROUPS_ADMIN_LIB . '/class-groups-admin-posts.php' );
|
||||
require_once( GROUPS_ADMIN_LIB . '/class-groups-admin-post-columns.php' );
|
||||
if ( $groups_legacy_enable ) {
|
||||
require_once GROUPS_LEGACY_LIB . '/admin/class-groups-admin-posts-legacy.php';
|
||||
require_once GROUPS_LEGACY_LIB . '/admin/class-groups-admin-post-columns-legacy.php';
|
||||
}
|
||||
}
|
||||
|
||||
// help
|
||||
if ( is_admin() ) {
|
||||
require_once( GROUPS_CORE_LIB . '/class-groups-help.php' );
|
||||
}
|
||||
|
||||
require_once( GROUPS_CORE_LIB . '/class-groups-capability.php' );
|
||||
require_once( GROUPS_CORE_LIB . '/class-groups-group.php' );
|
||||
require_once( GROUPS_CORE_LIB . '/class-groups-group-capability.php' );
|
||||
require_once( GROUPS_CORE_LIB . '/class-groups-user.php' );
|
||||
require_once( GROUPS_CORE_LIB . '/class-groups-user-capability.php' );
|
||||
require_once( GROUPS_CORE_LIB . '/class-groups-user-group.php' );
|
||||
|
||||
/**
|
||||
* Load auto :
|
||||
*/
|
||||
|
||||
require_once( GROUPS_AUTO_LIB . '/class-groups-registered.php' );
|
||||
|
||||
/**
|
||||
* Load access :
|
||||
*/
|
||||
|
||||
require_once( GROUPS_ACCESS_LIB . '/class-groups-post-access.php' );
|
||||
if ( $groups_legacy_enable ) {
|
||||
require_once GROUPS_LEGACY_LIB . '/access/class-groups-post-access-legacy.php';
|
||||
}
|
||||
require_once GROUPS_ACCESS_LIB . '/class-groups-comment-access.php';
|
||||
|
||||
if ( is_admin() ) {
|
||||
require_once( GROUPS_ACCESS_LIB . '/class-groups-access-meta-boxes.php' );
|
||||
if ( $groups_legacy_enable ) {
|
||||
require_once( GROUPS_LEGACY_LIB . '/access/class-groups-access-meta-boxes-legacy.php' );
|
||||
}
|
||||
}
|
||||
require_once( GROUPS_ACCESS_LIB . '/class-groups-access-shortcodes.php' );
|
||||
require_once( GROUPS_VIEWS_LIB . '/class-groups-shortcodes.php' );
|
||||
|
||||
/**
|
||||
* Load wp :
|
||||
*/
|
||||
require_once( GROUPS_WP_LIB . '/class-groups-wordpress.php' );
|
||||
|
||||
/**
|
||||
* Extras ...
|
||||
*/
|
||||
require_once GROUPS_EXTRA_LIB . '/class-groups-extra.php';
|
||||
|
||||
// widgets
|
||||
// include_once( GROUPS_CORE_LIB . '/class-groups-widgets.php' );
|
||||
// add_action( 'widgets_init', 'groups_widgets_init' );
|
||||
|
||||
/**
|
||||
* Register widgets
|
||||
*/
|
||||
// function groups_widgets_init() {
|
||||
// register_widget( 'Groups_Widget' );
|
||||
// }
|
||||
|
||||
/**
|
||||
* Returns the prefixed DB table name.
|
||||
* @param string $name the name of the DB table
|
||||
* @return string prefixed DB table name
|
||||
*/
|
||||
function _groups_get_tablename( $name ) {
|
||||
global $wpdb;
|
||||
return $wpdb->prefix . GROUPS_TP . $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* This returns true if admin override is enabled and the current user
|
||||
* is an administrator, otherwise false.
|
||||
* To enable admin override (AKA god mode for admins), add this to
|
||||
* your wp-config.php :
|
||||
*
|
||||
* define( 'GROUPS_ADMINISTRATOR_OVERRIDE', true );
|
||||
*
|
||||
* Enabling this is NOT recommended for production sites.
|
||||
*
|
||||
* @param int $user_id indicate the user ID or omit to check for the current user
|
||||
* @return boolean
|
||||
*/
|
||||
function _groups_admin_override( $user_id = null ) {
|
||||
$result = false;
|
||||
if ( ( $user_id === null ) && function_exists( 'get_current_user_id' ) ) {
|
||||
$user_id = get_current_user_id();
|
||||
}
|
||||
if ( $user_id ) {
|
||||
if ( defined( 'GROUPS_ADMINISTRATOR_OVERRIDE' ) && ( GROUPS_ADMINISTRATOR_OVERRIDE === true ) ) {
|
||||
if ( user_can( $user_id, 'administrator' ) ) {
|
||||
$result = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
/**
|
||||
* class-groups-extra.php
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 2.1.2
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compatibility actions, filters, etc as needed.
|
||||
*/
|
||||
class Groups_Extra {
|
||||
|
||||
/**
|
||||
* Registers actions, filters ...
|
||||
*/
|
||||
public static function init() {
|
||||
add_filter( 'woocommerce_product_is_visible', array( __CLASS__, 'woocommerce_product_is_visible' ), 10, 2 );
|
||||
add_filter( 'groups_comment_access_comment_count_where', array( __CLASS__, 'groups_comment_access_comment_count_where'), 10, 2 );
|
||||
add_filter( 'groups_post_access_posts_where_query_get_post_types', array( __CLASS__, 'groups_post_access_posts_where_query_get_post_types' ), 10, 3 );
|
||||
}
|
||||
|
||||
/**
|
||||
* Up-sell and cross-sell products are obtained directly by their ids and
|
||||
* no normal filters are executed that would hide them. This filter is used
|
||||
* instead to determine the visibility.
|
||||
*
|
||||
* If at some point we had a get_post filter in WordPress, it could filter these
|
||||
* and we wouldn't need this.
|
||||
*
|
||||
* @param boolean $visible
|
||||
* @param int $product_id
|
||||
* @return boolean
|
||||
*/
|
||||
public static function woocommerce_product_is_visible( $visible, $product_id ) {
|
||||
if ( $visible ) {
|
||||
$visible = Groups_Post_Access::user_can_read_post( $product_id );
|
||||
}
|
||||
return $visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Take WooCommerce comment types into account.
|
||||
*
|
||||
* @param string $where
|
||||
* @param int $post_id
|
||||
* @return string
|
||||
*/
|
||||
public static function groups_comment_access_comment_count_where( $where, $post_id ) {
|
||||
if ( defined( 'WC_VERSION' ) ) {
|
||||
$where .= " AND comment_type NOT IN ('order_note', 'webhook_delivery') ";
|
||||
}
|
||||
return $where;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the query is a wc_query for product_query; if $post_types is empty, it will assume the product type and return that.
|
||||
*
|
||||
* @param string|array $post_types current query post types
|
||||
* @param string $where the where part of the query
|
||||
* @param WP_Query $query the query
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function groups_post_access_posts_where_query_get_post_types( $post_types, $where, $query ) {
|
||||
if (
|
||||
empty( $post_types ) ||
|
||||
is_string( $post_types ) && ( $post_types === '' ) ||
|
||||
is_array( $post_types ) && ( count( $post_types ) === 0 )
|
||||
) {
|
||||
$wc_query = $query->get( 'wc_query', null );
|
||||
if ( ! empty( $wc_query ) && $wc_query === 'product_query' ) {
|
||||
$post_types = 'product';
|
||||
}
|
||||
}
|
||||
return $post_types;
|
||||
}
|
||||
}
|
||||
add_action( 'init', array( 'Groups_Extra', 'init' ) );
|
||||
@@ -0,0 +1,423 @@
|
||||
<?php
|
||||
/**
|
||||
* groups-tests.php
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.0.0
|
||||
*/
|
||||
// bootstrap WordPress
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
$wp_load = 'wp-load.php';
|
||||
$max_depth = 100; // prevent death by depth
|
||||
while ( !file_exists( $wp_load ) && ( $max_depth > 0 ) ) {
|
||||
$wp_load = '../' . $wp_load;
|
||||
$max_depth--;
|
||||
}
|
||||
if ( file_exists( $wp_load ) ) {
|
||||
require_once $wp_load;
|
||||
}
|
||||
}
|
||||
if ( defined( 'ABSPATH' ) ) {
|
||||
|
||||
function groups_tests() {
|
||||
|
||||
assert_options( ASSERT_ACTIVE, true );
|
||||
assert_options( ASSERT_WARNING, true );
|
||||
assert_options( ASSERT_BAIL, false );
|
||||
|
||||
//
|
||||
// PART 1 : create test data
|
||||
//
|
||||
|
||||
// *** groups ***
|
||||
|
||||
// create valid test groups
|
||||
//
|
||||
// Fruits [dance] {foo}
|
||||
// / \
|
||||
// Sweet {dol} Sour [sing]
|
||||
// | |
|
||||
// Banana {baz} Lemon {bar}
|
||||
//
|
||||
// All groups can dance.
|
||||
// Only Sour and Lemon can sing.
|
||||
$fruits_group_id = Groups_Group::create( array( 'name' => 'Fruits' ) );
|
||||
assert( '$fruits_group_id !== false' );
|
||||
$sweet_group_id = Groups_Group::create( array( 'name' => 'Sweet', 'parent_id' => $fruits_group_id ) );
|
||||
assert( '$sweet_group_id !== false' );
|
||||
$sour_group_id = Groups_Group::create( array( 'name' => 'Sour', 'parent_id' => $fruits_group_id ) );
|
||||
assert( '$sour_group_id !== false' );
|
||||
$lemon_group_id = Groups_Group::create( array( 'name' => 'Lemon', 'parent_id' => $sour_group_id ) );
|
||||
assert( '$lemon_group_id !== false' );
|
||||
$banana_group_id = Groups_Group::create( array( 'name' => 'Banana', 'parent_id' => $sweet_group_id ) );
|
||||
assert( '$banana_group_id !== false' );
|
||||
|
||||
// fail to create group with missing name
|
||||
$bogus_group_id = Groups_Group::create( array( ) );
|
||||
assert( '$bogus_group_id === false; /* empty name */' );
|
||||
|
||||
// fail to create group with wrong parent_id
|
||||
$bogus_group_id = Groups_Group::create( array( 'name' => 'bogus', 'parent_id' => -1 ) );
|
||||
assert( '$bogus_group_id === false; /* wrong parent_id */' );
|
||||
|
||||
// *** capabilities ***
|
||||
$sing_capability_id = Groups_Capability::create( array( 'capability' => 'sing' ) );
|
||||
assert( '$sing_capability_id !== false' );
|
||||
|
||||
$dance_capability_id = Groups_Capability::create( array( 'capability' => 'dance' ) );
|
||||
assert( '$dance_capability_id !== false' );
|
||||
|
||||
$clap_capability_id = Groups_Capability::create( array( 'capability' => 'clap' ) );
|
||||
assert( '$clap_capability_id !== false' );
|
||||
|
||||
// read capability by id
|
||||
assert( 'Groups_Capability::read( $sing_capability_id )' );
|
||||
|
||||
// read capability by unique label
|
||||
assert( 'Groups_Capability::read_by_capability( "dance" )' );
|
||||
|
||||
|
||||
// *** users ***
|
||||
|
||||
// create test users
|
||||
$fooname = 'foo' . rand(0, 100);
|
||||
$foo_user_id = wp_create_user( $fooname, 'foo', $fooname . '@example.com' );
|
||||
assert( '$foo_user_id instanceof WP_Error === false');
|
||||
|
||||
$barname = 'bar' . rand(0, 100);
|
||||
$bar_user_id = wp_create_user( $barname, 'bar', $barname . '@example.com' );
|
||||
assert( '$bar_user_id instanceof WP_Error === false');
|
||||
|
||||
// this user is used to test the automatic resolution of its relationship
|
||||
// with the banana group when the group is deleted
|
||||
// it's also used to test automatic resolution of its "clap" capability
|
||||
// after that capability has been deleted
|
||||
$bazname = 'baz' . rand(0, 100);
|
||||
$baz_user_id = wp_create_user( $bazname, 'baz', $bazname . '@example.com' );
|
||||
assert( '$baz_user_id instanceof WP_Error === false');
|
||||
|
||||
// this user is deleted, the group relation must be deleted automatically
|
||||
$dolname = 'dol' . rand(0, 100);
|
||||
$dol_user_id = wp_create_user( $dolname, 'dol', $dolname . ' @example.com' );
|
||||
assert( '$dol_user_id instanceof WP_Error === false');
|
||||
|
||||
// this user is a simple editor, used to test WordPress capabilities
|
||||
$editorname = 'rotide' . rand(0, 100);
|
||||
$editor_user_id = wp_create_user( $editorname, 'rotide', $editorname . '@example.com' );
|
||||
assert( '$editor_user_id instanceof WP_Error === false');
|
||||
if ( !( $editor_user_id instanceof WP_Error ) ) {
|
||||
$editor_user = new WP_User( $editor_user_id );
|
||||
$editor_user->set_role( 'editor' );
|
||||
} else {
|
||||
$editor_user_id = false;
|
||||
}
|
||||
|
||||
// *** users & groups ***
|
||||
|
||||
// add valid users to groups
|
||||
// echo "foo user id: $foo_user_id group: $fruits_group_id<br/>";
|
||||
assert( 'Groups_User_Group::create(array( "user_id" => $foo_user_id, "group_id" => $fruits_group_id ) )' );
|
||||
assert( 'Groups_User_Group::create(array( "user_id" => $bar_user_id, "group_id" => $lemon_group_id ) )' );
|
||||
assert( 'Groups_User_Group::create(array( "user_id" => $baz_user_id, "group_id" => $banana_group_id ) )' );
|
||||
assert( 'Groups_User_Group::create(array( "user_id" => $dol_user_id, "group_id" => $sweet_group_id ) )' );
|
||||
|
||||
// add invalid user to group
|
||||
assert( 'Groups_User_Group::create(array( "user_id" => -1, "group_id" => $sweet_group_id ) ) === false' );
|
||||
|
||||
// add valid user to invalid group
|
||||
assert( 'Groups_User_Group::create(array( "user_id" => $dol_user_id, "group_id" => -1 ) ) === false' );
|
||||
|
||||
// define capabilities for groups
|
||||
assert( 'Groups_Group_Capability::create( array( "group_id" => $fruits_group_id, "capability_id" => $dance_capability_id ) )' );
|
||||
assert( 'Groups_Group_Capability::create( array( "group_id" => $sour_group_id, "capability_id" => $sing_capability_id ) )' );
|
||||
|
||||
// define capabilities for users
|
||||
assert( 'Groups_User_Capability::create( array( "user_id" => $foo_user_id, "capability_id" => $clap_capability_id ) )' );
|
||||
assert( 'Groups_User_Capability::create( array( "user_id" => $baz_user_id, "capability_id" => $clap_capability_id ) )' );
|
||||
|
||||
// check groups that can dance (all)
|
||||
// just reading will not check the hierarchy of course ...
|
||||
assert( 'Groups_Group_Capability::read( $fruits_group_id, $dance_capability_id )' );
|
||||
assert( 'Groups_Group_Capability::read( $sweet_group_id, $dance_capability_id ) === false' );
|
||||
assert( 'Groups_Group_Capability::read( $banana_group_id, $dance_capability_id ) === false' );
|
||||
assert( 'Groups_Group_Capability::read( $sour_group_id, $dance_capability_id ) === false' );
|
||||
assert( 'Groups_Group_Capability::read( $lemon_group_id, $dance_capability_id ) === false' );
|
||||
// same for check on groups that can sing
|
||||
assert( 'Groups_Group_Capability::read( $fruits_group_id, $sing_capability_id ) === false' );
|
||||
assert( 'Groups_Group_Capability::read( $sweet_group_id, $sing_capability_id ) === false' );
|
||||
assert( 'Groups_Group_Capability::read( $banana_group_id, $sing_capability_id ) === false' );
|
||||
assert( 'Groups_Group_Capability::read( $sour_group_id, $sing_capability_id )' );
|
||||
assert( 'Groups_Group_Capability::read( $lemon_group_id, $sing_capability_id ) === false' );
|
||||
|
||||
// hierarchical groups
|
||||
$fruits_group = new Groups_Group( $fruits_group_id );
|
||||
$sweet_group = new Groups_Group( $sweet_group_id );
|
||||
$banana_group = new Groups_Group( $banana_group_id );
|
||||
$sour_group = new Groups_Group( $sour_group_id );
|
||||
$lemon_group = new Groups_Group( $lemon_group_id );
|
||||
|
||||
// retrieve users
|
||||
assert( 'count( $fruits_group->users ) > 0' );
|
||||
|
||||
// all should be able to "dance" : check by capability label ...
|
||||
assert( '$fruits_group->can( "dance" )' );
|
||||
assert( '$sweet_group->can( "dance" )' );
|
||||
assert( '$banana_group->can( "dance" )' );
|
||||
assert( '$sour_group->can( "dance" )' );
|
||||
assert( '$lemon_group->can( "dance" )' );
|
||||
// ... or id
|
||||
assert( '$fruits_group->can( $dance_capability_id )' );
|
||||
assert( '$sweet_group->can( $dance_capability_id )' );
|
||||
assert( '$banana_group->can( $dance_capability_id )' );
|
||||
assert( '$sour_group->can( $dance_capability_id )' );
|
||||
assert( '$lemon_group->can( $dance_capability_id )' );
|
||||
|
||||
// only sour and lemon can sing:
|
||||
assert( '!$fruits_group->can( "sing" )' );
|
||||
assert( '!$sweet_group->can( "sing" )' );
|
||||
assert( '!$banana_group->can( "sing" )' );
|
||||
assert( '$sour_group->can( "sing" )' );
|
||||
assert( '$lemon_group->can( "sing" )' );
|
||||
// ... or id
|
||||
assert( '!$fruits_group->can( $sing_capability_id )' );
|
||||
assert( '!$sweet_group->can( $sing_capability_id )' );
|
||||
assert( '!$banana_group->can( $sing_capability_id )' );
|
||||
assert( '$sour_group->can( $sing_capability_id )' );
|
||||
assert( '$lemon_group->can( $sing_capability_id )' );
|
||||
|
||||
// no group can clap
|
||||
assert( '!$fruits_group->can( $clap_capability_id )' );
|
||||
assert( '!$sweet_group->can( $clap_capability_id )' );
|
||||
assert( '!$banana_group->can( $clap_capability_id )' );
|
||||
assert( '!$sour_group->can( $clap_capability_id )' );
|
||||
assert( '!$lemon_group->can( $clap_capability_id )' );
|
||||
|
||||
// user capabilities
|
||||
$foo = new Groups_User( $foo_user_id );
|
||||
$dol = new Groups_User( $dol_user_id );
|
||||
$baz = new Groups_User( $baz_user_id );
|
||||
$bar = new Groups_User( $bar_user_id );
|
||||
|
||||
assert( '$foo->can( "dance" )' );
|
||||
assert( '$dol->can( "dance" )' );
|
||||
assert( '$baz->can( "dance" )' );
|
||||
assert( '$bar->can( "dance" )' );
|
||||
|
||||
assert( '$foo->can( $dance_capability_id )' );
|
||||
assert( '$dol->can( $dance_capability_id )' );
|
||||
assert( '$baz->can( $dance_capability_id )' );
|
||||
assert( '$bar->can( $dance_capability_id )' );
|
||||
|
||||
assert( '!$foo->can( "sing" )' );
|
||||
assert( '!$dol->can( "sing" )' );
|
||||
assert( '!$baz->can( "sing" )' );
|
||||
assert( '$bar->can( "sing" )' );
|
||||
|
||||
assert( '!$foo->can( $sing_capability_id )' );
|
||||
assert( '!$dol->can( $sing_capability_id )' );
|
||||
assert( '!$baz->can( $sing_capability_id )' );
|
||||
assert( '$bar->can( $sing_capability_id )' );
|
||||
|
||||
// only foo & baz can clap
|
||||
assert( '$foo->can( "clap" )' );
|
||||
assert( '!$dol->can( "clap" )' );
|
||||
assert( '$baz->can( "clap" )' );
|
||||
assert( '!$bar->can( "clap" )' );
|
||||
|
||||
assert( '$foo->can( $clap_capability_id )' );
|
||||
assert( '!$dol->can( $clap_capability_id )' );
|
||||
assert( '$baz->can( $clap_capability_id )' );
|
||||
assert( '!$bar->can( $clap_capability_id )' );
|
||||
|
||||
// user can not what is not defined
|
||||
assert( '!$foo->can( null )' );
|
||||
assert( '!$dol->can( null )' );
|
||||
assert( '!$baz->can( null )' );
|
||||
assert( '!$bar->can( null )' );
|
||||
|
||||
assert( '!$foo->can( "bogus" )' );
|
||||
assert( '!$dol->can( "bogus" )' );
|
||||
assert( '!$baz->can( "bogus" )' );
|
||||
assert( '!$bar->can( "bogus" )' );
|
||||
|
||||
// groups can not what is not defined
|
||||
assert( '!$fruits_group->can( null )' );
|
||||
assert( '!$sweet_group->can( null )' );
|
||||
assert( '!$banana_group->can( null )' );
|
||||
assert( '!$sour_group->can( null )' );
|
||||
assert( '!$lemon_group->can( null )' );
|
||||
|
||||
assert( '!$fruits_group->can( "bogus" )' );
|
||||
assert( '!$sweet_group->can( "bogus" )' );
|
||||
assert( '!$banana_group->can( "bogus" )' );
|
||||
assert( '!$sour_group->can( "bogus" )' );
|
||||
assert( '!$lemon_group->can( "bogus" )' );
|
||||
|
||||
// test WordPress capabilities
|
||||
$administrator = new Groups_User( 1 );
|
||||
assert( '$administrator->can( "activate_plugins" )' );
|
||||
|
||||
if ( $editor_user_id ) {
|
||||
$editor = new Groups_User( $editor_user_id );
|
||||
assert( '$editor->can( "edit_posts" )' );
|
||||
assert( '!$editor->can( "activate_plugins" )' );
|
||||
}
|
||||
|
||||
if ( is_multisite() ) {
|
||||
// $randext = rand( 0, 100 );
|
||||
// $wpmu_test_user_id = wp_create_user( 'wpmu_test_user' . $randext, 'wpmu_test_user' );
|
||||
// assert( '$wpmu_test_user_id instanceof WP_Error === false');
|
||||
// @todo create a blog => must create new tables
|
||||
// wpmu_create_blog( "groups_wpmu_" . $randext, "groups_wpmu_" . $randext, "Groups WPMU Test", $wpmu_test_user_id );
|
||||
// @todo add user to new blog
|
||||
// @todo switch to new blog
|
||||
// @todo check that new user is in "Registered" group
|
||||
// @todo switch to current blog
|
||||
}
|
||||
|
||||
//
|
||||
// PART 2 : delete test data
|
||||
//
|
||||
|
||||
if ( is_multisite() ) {
|
||||
// @todo delete new blog
|
||||
}
|
||||
|
||||
// remove capabilities from groups
|
||||
assert( 'Groups_Group_Capability::delete( $fruits_group_id, $dance_capability_id )' );
|
||||
|
||||
// remove users from groups
|
||||
assert( 'Groups_User_Group::delete($foo_user_id, $fruits_group_id)' );
|
||||
assert( 'Groups_User_Group::delete($bar_user_id, $lemon_group_id)' );
|
||||
// baz must be deleted from user_group when banana group is deleted
|
||||
|
||||
// invalid remove user from group
|
||||
assert( 'Groups_User_Group::delete($foo_user_id, $banana_group_id) === false' );
|
||||
|
||||
// delete test users
|
||||
include_once( ABSPATH . '/wp-admin/includes/user.php' );
|
||||
if ( $foo_user_id && !( $foo_user_id instanceof WP_Error ) ) {
|
||||
assert( 'wp_delete_user( $foo_user_id ) === true' );
|
||||
}
|
||||
if ( $bar_user_id && !( $bar_user_id instanceof WP_Error ) ) {
|
||||
assert( 'wp_delete_user( $bar_user_id ) === true' );
|
||||
}
|
||||
if ( $dol_user_id && !( $dol_user_id instanceof WP_Error ) ) {
|
||||
assert( 'wp_delete_user( $dol_user_id ) === true' );
|
||||
if ( $sweet_group_id ) {
|
||||
// see above, this user must have been removed from the group upon its deletion
|
||||
assert( 'Groups_User_Group::read( $dol_user_id, $sweet_group_id ) === false' );
|
||||
}
|
||||
}
|
||||
if ( $editor_user_id && !( $editor_user_id instanceof WP_Error ) ) {
|
||||
assert( 'wp_delete_user( $editor_user_id ) === true' );
|
||||
}
|
||||
// fail to delete inexisting capabilities
|
||||
assert( 'Groups_Capability::delete( -1 ) === false' );
|
||||
|
||||
// delete valid test capabilities
|
||||
if ( $sing_capability_id ) {
|
||||
assert( 'Groups_Capability::delete( $sing_capability_id ) !== false' );
|
||||
}
|
||||
if ( $dance_capability_id ) {
|
||||
assert( 'Groups_Capability::delete( $dance_capability_id ) !== false' );
|
||||
}
|
||||
if ( $clap_capability_id ) {
|
||||
assert( 'Groups_Capability::delete( $clap_capability_id ) !== false' );
|
||||
}
|
||||
// baz shouldn't be able to clap if there's no clapping capability anymore
|
||||
if ( $baz_user_id && !( $baz_user_id instanceof WP_Error ) ) {
|
||||
assert( '!$baz->can( "clap" )' );
|
||||
}
|
||||
|
||||
// fail to delete inexisting group
|
||||
assert( 'Groups_Group::delete( -1 ) === false' );
|
||||
|
||||
// delete invalid test group if creation was successful
|
||||
if ( $bogus_group_id ) {
|
||||
assert( 'Groups_Group::delete( $bogus_group_id )' );
|
||||
}
|
||||
|
||||
// delete valid test groups
|
||||
if ( $fruits_group_id ) {
|
||||
assert( 'Groups_Group::delete( $fruits_group_id )' );
|
||||
}
|
||||
if ( $sweet_group_id ) {
|
||||
assert( 'Groups_Group::delete( $sweet_group_id )' );
|
||||
}
|
||||
if ( $sour_group_id ) {
|
||||
assert( 'Groups_Group::delete( $sour_group_id )' );
|
||||
}
|
||||
if ( $lemon_group_id ) {
|
||||
assert( 'Groups_Group::delete( $lemon_group_id )');
|
||||
}
|
||||
if ( $banana_group_id ) {
|
||||
assert( 'Groups_Group::delete( $banana_group_id )' );
|
||||
assert( 'Groups_User_Group::delete($baz_user_id, $banana_group_id ) === false' );
|
||||
}
|
||||
// this user must not be deleted before as it is used to test that its
|
||||
// relationship with the banana group is resolved when the group is deleted
|
||||
if ( $baz_user_id && !( $baz_user_id instanceof WP_Error ) ) {
|
||||
assert( 'wp_delete_user( $baz_user_id ) === true' );
|
||||
}
|
||||
}
|
||||
|
||||
$active_plugins = get_option( 'active_plugins', array() );
|
||||
$active_sitewide_plugins = array();
|
||||
if ( is_multisite() ) {
|
||||
$active_sitewide_plugins = get_site_option( 'active_sitewide_plugins', array() );
|
||||
}
|
||||
if ( in_array( 'groups/groups.php', $active_plugins ) || key_exists( 'groups/groups.php', $active_sitewide_plugins ) ) {
|
||||
if ( !current_user_can( GROUPS_ADMINISTER_GROUPS ) ) {
|
||||
wp_die( __( 'Access denied.', 'groups' ) );
|
||||
} else {
|
||||
$run = isset( $_POST['run'] ) ? $_POST['run'] : null;
|
||||
switch( $run ) {
|
||||
case 'run' :
|
||||
if ( !isset( $_POST['groups-test-nonce'] ) || !wp_verify_nonce( $_POST['groups-test-nonce'], 'run-tests' ) ) {
|
||||
wp_die( __( 'Access denied.', 'groups' ) );
|
||||
}
|
||||
echo '<h1>Running tests for <i>Groups</i> plugin ...</h1>';
|
||||
groups_tests();
|
||||
echo '<h2>Finished.</h2>';
|
||||
break;
|
||||
default :
|
||||
$url = get_bloginfo( 'url' );
|
||||
echo '<p style="color:#f00; font-weight:bold;">';
|
||||
echo 'DO NOT CONTINUE UNLESS YOU KNOW WHAT YOU ARE DOING.';
|
||||
echo '</p>';
|
||||
echo '<ul>';
|
||||
echo '<li>This will run tests for the Groups plugin on this site.</li>';
|
||||
echo '<li>Unless you are a developer who knows what she or he is doing, you do not need to do this and you do not want to proceed.</li>';
|
||||
echo '<li>It may <strong>completely destroy your site</strong>.</li>';
|
||||
echo '<li>Run only at your own risk, do not blame anyone if something goes wrong.</li>';
|
||||
echo '<li>You <strong>agree to be solely responsible for any damage</strong> this may cause to the site.';
|
||||
echo '<li>Make a full backup of your site and database before you continue.</li>';
|
||||
echo '<li>If in doubt, <strong><a href="' . $url . '">do not continue</a></strong>.</li>';
|
||||
echo '</ul>';
|
||||
echo '<form action="" method="post">';
|
||||
echo '<input name="run" value="run" type="hidden" />';
|
||||
echo '<input type="submit" value="Go" />';
|
||||
wp_nonce_field( 'run-tests', 'groups-test-nonce', true, true );
|
||||
echo '</form>';
|
||||
}
|
||||
|
||||
}
|
||||
} else {
|
||||
echo 'The <i>Groups</i> plugin is not active, not running tests.';
|
||||
}
|
||||
|
||||
} // ABSPATH defined
|
||||
@@ -0,0 +1,607 @@
|
||||
<?php
|
||||
/**
|
||||
* class-groups-shortcodes.php
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.0.0
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Shortcode handlers
|
||||
*/
|
||||
class Groups_Shortcodes {
|
||||
|
||||
/**
|
||||
* Adds shortcodes.
|
||||
*/
|
||||
public static function init() {
|
||||
// login
|
||||
add_shortcode( 'groups_login', array( __CLASS__, 'groups_login' ) );
|
||||
// logout
|
||||
add_shortcode( 'groups_logout', array( __CLASS__, 'groups_logout' ) );
|
||||
// group info
|
||||
add_shortcode( 'groups_group_info', array( __CLASS__, 'groups_group_info' ) );
|
||||
// user groups
|
||||
add_shortcode( 'groups_user_groups', array( __CLASS__, 'groups_user_groups' ) );
|
||||
// groups
|
||||
add_shortcode( 'groups_groups', array( __CLASS__, 'groups_groups' ) );
|
||||
// join a group
|
||||
add_shortcode( 'groups_join', array( __CLASS__, 'groups_join' ) );
|
||||
// leave a group
|
||||
add_shortcode( 'groups_leave', array( __CLASS__, 'groups_leave' ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the Groups login form.
|
||||
*
|
||||
* The user is redirected to the current page after login by default.
|
||||
* The user can be redirected to a specific URL after login by
|
||||
* indicating the <code>redirect</code> attribute.
|
||||
*
|
||||
* @param array $atts
|
||||
* @param string $content
|
||||
* @return string the rendered form or empty
|
||||
*/
|
||||
public static function groups_login( $atts, $content = null ) {
|
||||
$current_url = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
|
||||
extract(
|
||||
shortcode_atts(
|
||||
array(
|
||||
'redirect' => $current_url,
|
||||
'show_logout' => 'no'
|
||||
),
|
||||
$atts
|
||||
)
|
||||
);
|
||||
$redirect = trim( $redirect );
|
||||
$show_logout = trim( strtolower( $show_logout ) );
|
||||
$output = '';
|
||||
if ( !is_user_logged_in() ) {
|
||||
$output .= wp_login_form(
|
||||
array(
|
||||
'echo' => false,
|
||||
'redirect' => $redirect
|
||||
)
|
||||
);
|
||||
} else {
|
||||
if ( $show_logout == 'yes' ) {
|
||||
$output .= self::groups_logout(
|
||||
array(
|
||||
'redirect' => $redirect
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the Groups logout link.
|
||||
*
|
||||
* The link is rendered if the user is logged in.
|
||||
* The user is redirected to the current page after logout by default.
|
||||
* The user can be redirected to a specific URL after logout by
|
||||
* indicating the <code>redirect</code> attribute.
|
||||
*
|
||||
* @param array $atts
|
||||
* @param string $content not used
|
||||
* @return string logout link, is empty if not logged in
|
||||
*/
|
||||
public static function groups_logout( $atts, $content = null ) {
|
||||
$current_url = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
|
||||
extract(
|
||||
shortcode_atts(
|
||||
array(
|
||||
'redirect' => $current_url
|
||||
),
|
||||
$atts
|
||||
)
|
||||
);
|
||||
$redirect = trim( $redirect );
|
||||
$output = '';
|
||||
if ( is_user_logged_in() ) {
|
||||
$output .= sprintf( '<a href="%s">', esc_url( wp_logout_url( $redirect ) ) );
|
||||
$output .= __( 'Log out', 'groups' );
|
||||
$output .= '</a>';
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders information about a group.
|
||||
* Attributes:
|
||||
* - "group" : group name or id
|
||||
* - "show" : what to show, can be "name", "description", "count"
|
||||
* - "format" :
|
||||
* - "single" : used with show="count", single form, defaults to '1'
|
||||
* - "plural" : used with show="count", plural form, defaults to '%d', must contain %d to show number
|
||||
*
|
||||
* @param array $atts attributes
|
||||
* @param string $content content to render
|
||||
* @return rendered information
|
||||
*/
|
||||
public static function groups_group_info( $atts, $content = null ) {
|
||||
global $wpdb;
|
||||
$output = "";
|
||||
$options = shortcode_atts(
|
||||
array(
|
||||
'group' => '',
|
||||
'show' => '',
|
||||
'format' => '',
|
||||
'single' => '1',
|
||||
'plural' => '%d'
|
||||
),
|
||||
$atts
|
||||
);
|
||||
$group = trim( $options['group'] );
|
||||
$current_group = Groups_Group::read( $group );
|
||||
if ( !$current_group ) {
|
||||
$current_group = Groups_Group::read_by_name( $group );
|
||||
}
|
||||
if ( $current_group ) {
|
||||
switch( $options['show'] ) {
|
||||
case 'name' :
|
||||
$output .= wp_filter_nohtml_kses( $current_group->name );
|
||||
break;
|
||||
case 'description' :
|
||||
$output .= wp_filter_nohtml_kses( $current_group->description );
|
||||
break;
|
||||
case 'count' :
|
||||
$user_group_table = _groups_get_tablename( 'user_group' );
|
||||
$count = $wpdb->get_var( $wpdb->prepare(
|
||||
"SELECT COUNT(*) FROM $user_group_table WHERE group_id = %d",
|
||||
Groups_Utility::id( $current_group->group_id )
|
||||
) );
|
||||
if ( $count === null ) {
|
||||
$count = 0;
|
||||
} else {
|
||||
$count = intval( $count );
|
||||
}
|
||||
$output .= _n( $options['single'], sprintf( $options['plural'], $count ), $count, 'groups' );
|
||||
break;
|
||||
// @todo experimental - could use pagination, sorting, link to profile, ...
|
||||
case 'users' :
|
||||
$user_group_table = _groups_get_tablename( 'user_group' );
|
||||
$users = $wpdb->get_results( $wpdb->prepare(
|
||||
"SELECT * FROM $wpdb->users LEFT JOIN $user_group_table ON $wpdb->users.ID = $user_group_table.user_id WHERE $user_group_table.group_id = %d",
|
||||
Groups_Utility::id( $current_group->group_id )
|
||||
) );
|
||||
if ( $users ) {
|
||||
$output .= '<ul>';
|
||||
foreach( $users as $user ) {
|
||||
$output .= '<li>' . wp_filter_nohtml_kses( $user->user_login ) . '</li>';
|
||||
}
|
||||
$output .= '</ul>';
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the current or a specific user's groups.
|
||||
* Attributes:
|
||||
* - "user_id" OR "user_login" OR "user_email" to identify the user, if none given assumes the current user
|
||||
* - "format" : one of "list" "div" "ul" or "ol" - "list" and "ul" are equivalent
|
||||
* - "list_class" : defaults to "groups"
|
||||
* - "item_class" : defaults to "name"
|
||||
* - "order_by" : defaults to "name", also accepts "group_id"
|
||||
* - "order" : default to "ASC", also accepts "asc", "desc" and "DESC"
|
||||
*
|
||||
* @param array $atts attributes
|
||||
* @param string $content not used
|
||||
* @return rendered groups for current user
|
||||
*/
|
||||
public static function groups_user_groups( $atts, $content = null ) {
|
||||
$output = '';
|
||||
$options = shortcode_atts(
|
||||
array(
|
||||
'user_id' => null,
|
||||
'user_login' => null,
|
||||
'user_email' => null,
|
||||
'format' => 'list',
|
||||
'list_class' => 'groups',
|
||||
'item_class' => 'name',
|
||||
'order_by' => 'name',
|
||||
'order' => 'ASC',
|
||||
'group' => null,
|
||||
'exclude_group' => null
|
||||
),
|
||||
$atts
|
||||
);
|
||||
$user_id = null;
|
||||
if ( $options['user_id'] !== null ) {
|
||||
if ( $user = get_user_by( 'id', $options['user_id'] ) ) {
|
||||
$user_id = $user->ID;
|
||||
}
|
||||
} else if ( $options['user_id'] !== null ) {
|
||||
if ( $user = get_user_by( 'login', $options['user_login'] ) ) {
|
||||
$user_id = $user->ID;
|
||||
}
|
||||
} else if ( $options['user_email'] !== null ) {
|
||||
if ( $user = get_user_by( 'email', $options['user_login'] ) ) {
|
||||
$user_id = $user->ID;
|
||||
}
|
||||
}
|
||||
if ( $user_id === null ) {
|
||||
$user_id = get_current_user_id();
|
||||
}
|
||||
if ( $user_id !== null ) {
|
||||
$user = new Groups_User( $user_id );
|
||||
$groups = $user->groups;
|
||||
|
||||
if ( !empty( $groups ) ) {
|
||||
// group attr
|
||||
if ( $options['group'] !== null ) {
|
||||
$groups = array();
|
||||
$groups_incl = explode( ',', $options['group'] );
|
||||
foreach ( $groups_incl as $group_incl ) {
|
||||
$group = trim( $group_incl );
|
||||
$current_group = Groups_Group::read( $group );
|
||||
if ( !$current_group ) {
|
||||
$current_group = Groups_Group::read_by_name( $group );
|
||||
}
|
||||
if ( $current_group ) {
|
||||
if ( Groups_User_Group::read( $user_id, $current_group->group_id ) ) {
|
||||
$groups[] = $current_group;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// exclude_group attr
|
||||
if ( $options['exclude_group'] !== null ) {
|
||||
$groups_excl = explode( ',', $options['exclude_group'] );
|
||||
foreach ( $groups_excl as $key => $group_excl ) {
|
||||
$group = trim( $group_excl );
|
||||
$current_group = Groups_Group::read( $group );
|
||||
if ( !$current_group ) {
|
||||
$current_group = Groups_Group::read_by_name( $group );
|
||||
}
|
||||
if ( $current_group ) {
|
||||
$groups_excl[$key] = $current_group->group_id;
|
||||
} else {
|
||||
unset( $groups_excl[$key] );
|
||||
}
|
||||
}
|
||||
foreach ( $groups as $key => $group ) {
|
||||
if ( in_array( $group->group_id, $groups_excl ) ) {
|
||||
unset( $groups[$key] );
|
||||
}
|
||||
}
|
||||
}
|
||||
switch( $options['order_by'] ) {
|
||||
case 'group_id' :
|
||||
usort( $groups, array( __CLASS__, 'sort_id' ) );
|
||||
break;
|
||||
default :
|
||||
usort( $groups, array( __CLASS__, 'sort_name' ) );
|
||||
}
|
||||
switch( $options['order'] ) {
|
||||
case 'desc' :
|
||||
case 'DESC' :
|
||||
$groups = array_reverse( $groups );
|
||||
break;
|
||||
}
|
||||
|
||||
switch( $options['format'] ) {
|
||||
case 'list' :
|
||||
case 'ul' :
|
||||
$output .= '<ul class="' . esc_attr( $options['list_class'] ) . '">';
|
||||
break;
|
||||
case 'ol' :
|
||||
$output .= '<ol class="' . esc_attr( $options['list_class'] ) . '">';
|
||||
break;
|
||||
default :
|
||||
$output .= '<div class="' . esc_attr( $options['list_class'] ) . '">';
|
||||
}
|
||||
foreach( $groups as $group ) {
|
||||
switch( $options['format'] ) {
|
||||
case 'list' :
|
||||
case 'ul' :
|
||||
case 'ol' :
|
||||
$output .= '<li class="' . esc_attr( $options['item_class'] ) . '">' . $group->name . '</li>';
|
||||
break;
|
||||
default :
|
||||
$output .= '<div class="' . esc_attr( $options['item_class'] ) . '">' . $group->name . '</div>';
|
||||
}
|
||||
}
|
||||
switch( $options['format'] ) {
|
||||
case 'list' :
|
||||
case 'ul' :
|
||||
$output .= '</ul>';
|
||||
break;
|
||||
case 'ol' :
|
||||
$output .= '</ol>';
|
||||
break;
|
||||
default :
|
||||
$output .= '</div>';
|
||||
}
|
||||
}
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Group comparison by group_id.
|
||||
*
|
||||
* @param Groups_Group $a
|
||||
* @param Groups_Group $b
|
||||
* @return int
|
||||
*/
|
||||
public static function sort_id( $a, $b ) {
|
||||
return $a->group_id - $b->group_id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Group comparison by name.
|
||||
*
|
||||
* @param Groups_Group $a
|
||||
* @param Groups_Group $b
|
||||
* @return int
|
||||
*/
|
||||
public static function sort_name( $a, $b ) {
|
||||
return strcmp( $a->name, $b->name );
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a list of the site's groups.
|
||||
* Attributes:
|
||||
* - "format" : one of "list" "div" "ul" or "ol" - "list" and "ul" are equivalent
|
||||
* - "list_class" : defaults to "groups"
|
||||
* - "item_class" : defaults to "name"
|
||||
* - "order_by" : defaults to "name", also accepts "group_id"
|
||||
* - "order" : default to "ASC", also accepts "asc", "desc" and "DESC"
|
||||
*
|
||||
* @param array $atts attributes
|
||||
* @param string $content not used
|
||||
* @return rendered groups
|
||||
*/
|
||||
public static function groups_groups( $atts, $content = null ) {
|
||||
global $wpdb;
|
||||
$output = '';
|
||||
$options = shortcode_atts(
|
||||
array(
|
||||
'format' => 'list',
|
||||
'list_class' => 'groups',
|
||||
'item_class' => 'name',
|
||||
'order_by' => 'name',
|
||||
'order' => 'ASC'
|
||||
),
|
||||
$atts
|
||||
);
|
||||
switch( $options['order_by'] ) {
|
||||
case 'group_id' :
|
||||
case 'name' :
|
||||
$order_by = $options['order_by'];
|
||||
break;
|
||||
default :
|
||||
$order_by = 'name';
|
||||
}
|
||||
switch( $options['order'] ) {
|
||||
case 'asc' :
|
||||
case 'ASC' :
|
||||
case 'desc' :
|
||||
case 'DESC' :
|
||||
$order = strtoupper( $options['order'] );
|
||||
break;
|
||||
default :
|
||||
$order = 'ASC';
|
||||
}
|
||||
$group_table = _groups_get_tablename( 'group' );
|
||||
if ( $groups = $wpdb->get_results(
|
||||
"SELECT group_id FROM $group_table ORDER BY $order_by $order"
|
||||
) ) {
|
||||
switch( $options['format'] ) {
|
||||
case 'list' :
|
||||
case 'ul' :
|
||||
$output .= '<ul class="' . esc_attr( $options['list_class'] ) . '">';
|
||||
break;
|
||||
case 'ol' :
|
||||
$output .= '<ol class="' . esc_attr( $options['list_class'] ) . '">';
|
||||
break;
|
||||
default :
|
||||
$output .= '<div class="' . esc_attr( $options['list_class'] ) . '">';
|
||||
}
|
||||
foreach( $groups as $group ) {
|
||||
$group = new Groups_Group( $group->group_id );
|
||||
switch( $options['format'] ) {
|
||||
case 'list' :
|
||||
case 'ul' :
|
||||
case 'ol' :
|
||||
$output .= '<li class="' . esc_attr( $options['item_class'] ) . '">' . $group->name . '</li>';
|
||||
break;
|
||||
default :
|
||||
$output .= '<div class="' . esc_attr( $options['item_class'] ) . '">' . $group->name . '</div>';
|
||||
}
|
||||
}
|
||||
switch( $options['format'] ) {
|
||||
case 'list' :
|
||||
case 'ul' :
|
||||
$output .= '</ul>';
|
||||
break;
|
||||
case 'ol' :
|
||||
$output .= '</ol>';
|
||||
break;
|
||||
default :
|
||||
$output .= '</div>';
|
||||
}
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a form that lets a user join a group.
|
||||
* * Attributes:
|
||||
* - "group" : (required) group name or id
|
||||
*
|
||||
* @param array $atts attributes
|
||||
* @param string $content not used
|
||||
*/
|
||||
public static function groups_join( $atts, $content = null ) {
|
||||
$nonce_action = 'groups_action';
|
||||
$nonce = 'nonce_join';
|
||||
$output = '';
|
||||
|
||||
$options = shortcode_atts(
|
||||
array(
|
||||
'group' => '',
|
||||
'display_message' => true,
|
||||
'display_is_member' => false,
|
||||
'submit_text' => __( 'Join the %s group', 'groups' )
|
||||
),
|
||||
$atts
|
||||
);
|
||||
extract( $options );
|
||||
|
||||
if ( $display_message === 'false' ) {
|
||||
$display_message = false;
|
||||
}
|
||||
if ( $display_is_member === 'true' ) {
|
||||
$display_is_member = true;
|
||||
}
|
||||
|
||||
$group = trim( $options['group'] );
|
||||
$current_group = Groups_Group::read( $group );
|
||||
if ( !$current_group ) {
|
||||
$current_group = Groups_Group::read_by_name( $group );
|
||||
}
|
||||
if ( $current_group ) {
|
||||
if ( $user_id = get_current_user_id() ) {
|
||||
$submitted = false;
|
||||
$invalid_nonce = false;
|
||||
if ( !empty( $_POST['groups_action'] ) && $_POST['groups_action'] == 'join' ) {
|
||||
$submitted = true;
|
||||
if ( !wp_verify_nonce( $_POST[$nonce], $nonce_action ) ) {
|
||||
$invalid_nonce = true;
|
||||
}
|
||||
}
|
||||
if ( $submitted && !$invalid_nonce ) {
|
||||
// add user to group
|
||||
if ( isset( $_POST['group_id'] ) ) {
|
||||
$join_group = Groups_Group::read( $_POST['group_id'] );
|
||||
Groups_User_Group::create(
|
||||
array(
|
||||
'group_id' => $join_group->group_id,
|
||||
'user_id' => $user_id
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
if ( !Groups_User_Group::read( $user_id, $current_group->group_id ) ) {
|
||||
$submit_text = sprintf( $options['submit_text'], wp_filter_nohtml_kses( $current_group->name ) );
|
||||
$output .= '<div class="groups-join">';
|
||||
$output .= '<form action="#" method="post">';
|
||||
$output .= '<input type="hidden" name="groups_action" value="join" />';
|
||||
$output .= '<input type="hidden" name="group_id" value="' . esc_attr( $current_group->group_id ) . '" />';
|
||||
$output .= '<input type="submit" value="' . $submit_text . '" />';
|
||||
$output .= wp_nonce_field( $nonce_action, $nonce, true, false );
|
||||
$output .= '</form>';
|
||||
$output .= '</div>';
|
||||
} else if ( $display_message ) {
|
||||
if ( $submitted && !$invalid_nonce && isset( $join_group ) && $join_group->group_id === $current_group->group_id ) {
|
||||
$output .= '<div class="groups-join joined">';
|
||||
$output .= sprintf( __( 'You have joined the %s group.', 'groups' ), wp_filter_nohtml_kses( $join_group->name ) );
|
||||
$output .= '</div>';
|
||||
}
|
||||
else if ( $display_is_member && isset( $current_group ) && $current_group !== false ) {
|
||||
$output .= '<div class="groups-join member">';
|
||||
$output .= sprintf( __( 'You are a member of the %s group.', 'groups' ), wp_filter_nohtml_kses( $current_group->name ) );
|
||||
$output .= '</div>';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a form that lets a user leave a group.
|
||||
* * Attributes:
|
||||
* - "group" : (required) group name or id
|
||||
*
|
||||
* @param array $atts attributes
|
||||
* @param string $content not used
|
||||
*/
|
||||
public static function groups_leave( $atts, $content = null ) {
|
||||
$nonce_action = 'groups_action';
|
||||
$nonce = 'nonce_leave';
|
||||
$output = '';
|
||||
|
||||
$options = shortcode_atts(
|
||||
array(
|
||||
'group' => '',
|
||||
'display_message' => true,
|
||||
'submit_text' => __( 'Leave the %s group', 'groups' ),
|
||||
),
|
||||
$atts
|
||||
);
|
||||
extract( $options );
|
||||
|
||||
if ( $display_message === 'false' ) {
|
||||
$display_message = false;
|
||||
}
|
||||
|
||||
$group = trim( $options['group'] );
|
||||
$current_group = Groups_Group::read( $group );
|
||||
if ( !$current_group ) {
|
||||
$current_group = Groups_Group::read_by_name( $group );
|
||||
}
|
||||
if ( $current_group ) {
|
||||
if ( $user_id = get_current_user_id() ) {
|
||||
$submitted = false;
|
||||
$invalid_nonce = false;
|
||||
if ( !empty( $_POST['groups_action'] ) && $_POST['groups_action'] == 'leave' ) {
|
||||
$submitted = true;
|
||||
if ( !wp_verify_nonce( $_POST[$nonce], $nonce_action ) ) {
|
||||
$invalid_nonce = true;
|
||||
}
|
||||
}
|
||||
if ( $submitted && !$invalid_nonce ) {
|
||||
// remove user from group
|
||||
if ( isset( $_POST['group_id'] ) ) {
|
||||
$leave_group = Groups_Group::read( $_POST['group_id'] );
|
||||
Groups_User_Group::delete( $user_id, $leave_group->group_id );
|
||||
}
|
||||
}
|
||||
if ( Groups_User_Group::read( $user_id, $current_group->group_id ) ) {
|
||||
$submit_text = sprintf( $options['submit_text'], wp_filter_nohtml_kses( $current_group->name ) );
|
||||
$output .= '<div class="groups-join">';
|
||||
$output .= '<form action="#" method="post">';
|
||||
$output .= '<input type="hidden" name="groups_action" value="leave" />';
|
||||
$output .= '<input type="hidden" name="group_id" value="' . esc_attr( $current_group->group_id ) . '" />';
|
||||
$output .= '<input type="submit" value="' . $submit_text . '" />';
|
||||
$output .= wp_nonce_field( $nonce_action, $nonce, true, false );
|
||||
$output .= '</form>';
|
||||
$output .= '</div>';
|
||||
} else if ( $display_message ) {
|
||||
if ( $submitted && !$invalid_nonce && isset( $leave_group ) && $leave_group->group_id === $current_group->group_id ) {
|
||||
$output .= '<div class="groups-join left">';
|
||||
$output .= sprintf( __( 'You have left the %s group.', 'groups' ), wp_filter_nohtml_kses( $leave_group->name ) );
|
||||
$output .= '</div>';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
Groups_Shortcodes::init();
|
||||
@@ -0,0 +1,131 @@
|
||||
<?php
|
||||
/**
|
||||
* class-groups-uie.php
|
||||
*
|
||||
* Copyright (c) "kento" Karim Rahimpur www.itthinx.com
|
||||
*
|
||||
* This code is released under the GNU General Public License.
|
||||
* See COPYRIGHT.txt and LICENSE.txt.
|
||||
*
|
||||
* This code 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.
|
||||
*
|
||||
* This header and all notices must be kept intact.
|
||||
*
|
||||
* @author Karim Rahimpur
|
||||
* @package groups
|
||||
* @since groups 1.3.14
|
||||
*/
|
||||
|
||||
if ( !defined( 'ABSPATH' ) ) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* User Interface Extensions.
|
||||
*
|
||||
* This class may yet be subject to changes in method signatures. External
|
||||
* dependency is not advised until the private access restriction is removed.
|
||||
*
|
||||
* @access private
|
||||
*/
|
||||
class Groups_UIE {
|
||||
|
||||
/**
|
||||
* Extension used for select
|
||||
* @var string
|
||||
*/
|
||||
private static $select = 'selectize';
|
||||
|
||||
/**
|
||||
* Setup.
|
||||
*/
|
||||
public static function init() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension chooser - determines what UI extension is used for an element.
|
||||
*
|
||||
* @param string $element choices: select
|
||||
* @param string $extension choices: selectize
|
||||
*/
|
||||
public static function set_extension( $element, $extension ) {
|
||||
switch( $element ) {
|
||||
case 'select' :
|
||||
self::$select = $extension;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue scripts and styles.
|
||||
*/
|
||||
public static function enqueue( $element = null ) {
|
||||
global $groups_version;
|
||||
switch( $element ) {
|
||||
case 'select' :
|
||||
switch ( self::$select ) {
|
||||
case 'selectize' :
|
||||
if ( !wp_script_is( 'selectize' ) ) {
|
||||
wp_enqueue_script( 'selectize', GROUPS_PLUGIN_URL . 'js/selectize/selectize.min.js', array( 'jquery' ), $groups_version, false );
|
||||
}
|
||||
if ( !wp_style_is( 'selectize' ) ) {
|
||||
wp_enqueue_style( 'selectize', GROUPS_PLUGIN_URL . 'css/selectize/selectize.bootstrap2.css', array(), $groups_version );
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Render select script and style.
|
||||
* @param string $selector identifying the select, default: select.groups-uie
|
||||
* @param boolean $script render the script, default: true
|
||||
* @param boolean $on_document_ready whether to trigger on document ready, default: true
|
||||
* @param boolean $create allow to create items, default: false (only with selectize)
|
||||
* @return string HTML
|
||||
*/
|
||||
public static function render_select( $selector = 'select.groups-uie', $script = true, $on_document_ready = true, $create = false ) {
|
||||
$output = '';
|
||||
if ( $script ) {
|
||||
$output .= '<script type="text/javascript">';
|
||||
$output .= 'if (typeof jQuery !== "undefined"){';
|
||||
if ( $on_document_ready ) {
|
||||
$output .= 'jQuery("document").ready(function(){';
|
||||
}
|
||||
switch( self::$select ) {
|
||||
case 'selectize' :
|
||||
$output .= sprintf(
|
||||
'jQuery("%s").selectize({%splugins: ["remove_button"]});',
|
||||
$selector,
|
||||
$create ? 'create:true,' : ''
|
||||
);
|
||||
break;
|
||||
}
|
||||
if ( $on_document_ready ) {
|
||||
$output .= '});';
|
||||
}
|
||||
$output .= '}'; // typeof jQuery
|
||||
$output .= '</script>';
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
public static function render_add_titles( $selector ) {
|
||||
$output = '<script type="text/javascript">';
|
||||
$output .= 'if ( typeof jQuery !== "undefined" ) {';
|
||||
$output .= sprintf( 'jQuery("%s").each(', $selector );
|
||||
$output .= 'function(){';
|
||||
$output .= 'var title = jQuery(this).html().replace( /(<\/[^>]+>)/igm , "$1 ");';
|
||||
$output .= 'jQuery(this).attr("title", this.innerText || jQuery(jQuery.parseHTML(title)).text().replace(/\s+/igm, " ") );';
|
||||
$output .= '}';
|
||||
$output .= ');';
|
||||
$output .= '}';
|
||||
$output .= '</script>';
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
Groups_UIE::init();
|
||||