├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── composer.json
├── readme.txt
├── wp-user-signups.php
└── wp-user-signups
├── assets
└── css
│ └── signups.css
└── includes
├── classes
├── class-wp-db-table-registration-log.php
├── class-wp-db-table-signupmeta.php
├── class-wp-db-table-signups.php
├── class-wp-db-table.php
├── class-wp-signup-query.php
├── class-wp-signup.php
└── class-wp-signups-list-table.php
└── functions
├── admin.php
├── assets.php
├── cache.php
├── capabilities.php
├── common.php
├── hooks.php
├── metadata.php
└── sponsor.php
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor/
2 | .DS_Store
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 5.0.1 (2021-03-21)
2 | * Improved PHP 7.4 support
3 |
4 | ## 5.0.0 (2020-05-05)
5 | * PHP 7.3 support
6 | * Rename text domain back to match WordPress.org plugin slug
7 |
8 | ## 4.0.0 (2017-04-11)
9 | * Database schema update
10 |
11 | ## 3.1.0 (2017-04-03)
12 | * Signup meta support
13 |
14 | ## 3.0.0 (2017-04-03)
15 | * Rebrand internally to WP Signups
16 |
17 | ## 2.0.0 (2017-03-15)
18 | * Extend WP_DB_Table class
19 |
20 | ## 1.2.0 (2017-03-03)
21 | * Fix bug with site column output
22 | * Single-site implementation improvements
23 | * Add descriptions to a few form fields
24 |
25 | ## 1.1.0 (2016-11-18)
26 | * Caching improvements
27 |
28 | ## 1.0.0 (2016-11-13)
29 | * Initial release
30 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 | {description}
294 | Copyright (C) {year} {fullname}
295 |
296 | This program is free software; you can redistribute it and/or modify
297 | it under the terms of the GNU General Public License as published by
298 | the Free Software Foundation; either version 2 of the License, or
299 | (at your option) any later version.
300 |
301 | This program is distributed in the hope that it will be useful,
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 | GNU General Public License for more details.
305 |
306 | You should have received a copy of the GNU General Public License along
307 | with this program; if not, write to the Free Software Foundation, Inc.,
308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309 |
310 | Also add information on how to contact you by electronic and paper mail.
311 |
312 | If the program is interactive, make it output a short notice like this
313 | when it starts in an interactive mode:
314 |
315 | Gnomovision version 69, Copyright (C) year name of author
316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 | This is free software, and you are welcome to redistribute it
318 | under certain conditions; type `show c' for details.
319 |
320 | The hypothetical commands `show w' and `show c' should show the appropriate
321 | parts of the General Public License. Of course, the commands you use may
322 | be called something other than `show w' and `show c'; they could even be
323 | mouse-clicks or menu items--whatever suits your program.
324 |
325 | You should also get your employer (if you work as a programmer) or your
326 | school, if any, to sign a "copyright disclaimer" for the program, if
327 | necessary. Here is a sample; alter the names:
328 |
329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
331 |
332 | {signature of Ty Coon}, 1 April 1989
333 | Ty Coon, President of Vice
334 |
335 | This General Public License does not permit incorporating your program into
336 | proprietary programs. If your program is a subroutine library, you may
337 | consider it more useful to permit linking proprietary applications with the
338 | library. If this is what you want to do, use the GNU Lesser General
339 | Public License instead of this License.
340 |
341 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # WP User Signups
2 |
3 | This is the best way to manage user (and site) sign-ups in WordPress.
4 |
5 | Use it to:
6 |
7 | * View & manage sign-ups the WordPress way
8 | * Resend activation emails to users who haven't received theirs yet
9 | * Manually edit & activate sign-ups for users who are having trouble
10 |
11 | # Installation
12 |
13 | * Download and install using the built in WordPress plugin installer.
14 | * Activate in the "Plugins" area of your network admin by clicking the "Activate" link.
15 | * Consider sponsoring future development by clicking "Sponsor".
16 | * No further setup or configuration is necessary.
17 |
18 | # FAQ
19 |
20 | ### Does this work with on single-site, multi-site, and multi-network installations?
21 |
22 | Yes. Yes. Yes.
23 |
24 | ### Does this work with BuddyPress, bbPress, and GlotPress?
25 |
26 | Yes. Yes. Yes.
27 |
28 | ### Does this work with other membership plugins?
29 |
30 | Ya know, I'm not really sure. Please test it with your favorite ones and let me know!
31 |
32 | ### Credits
33 |
34 | This plugin is largely inspired by:
35 |
36 | * BuddyPress
37 | * Unconfirmed
38 |
39 | ### Where can I get support?
40 |
41 | This plugin is free for anyone to use.
42 |
43 | * Community: https://wordpress.org/support/plugin/wp-user-signups
44 | * Development: https://github.com/stuttter/wp-user-signups/discussions
45 |
46 | ### Contributing
47 |
48 | Please [open a new issue](/pull/new/master) to discuss whether the feature is a good fit for the project. Once you've decided to work on a pull request, please follow the [WordPress Coding Standards](http://make.wordpress.org/core/handbook/coding-standards/).
49 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "stuttter/wp-user-signups",
3 | "description": "The best way to manage user & site sign-ups in WordPress",
4 | "homepage": "https://github.com/stuttter/wp-user-signups",
5 | "type": "wordpress-plugin",
6 | "require": {
7 | "php": ">=5.2",
8 | "composer/installers": "^1.0"
9 | },
10 | "license": "GPL-2.0-or-later",
11 | "authors": [
12 | {
13 | "name": "John James Jacoby",
14 | "email": "johnjamesjacoby@me.com"
15 | }
16 | ]
17 | }
18 |
--------------------------------------------------------------------------------
/readme.txt:
--------------------------------------------------------------------------------
1 | === WP User Signups ===
2 | Author: Triple J Software, Inc.
3 | Author URI: https://jjj.software
4 | Donate link: https://buy.stripe.com/7sI3cd2tK1Cy2lydQR
5 | Plugin URI: https://wordpress.org/plugins/wp-user-signups/
6 | License URI: https://www.gnu.org/licenses/gpl-2.0.html
7 | License: GPLv2 or later
8 | Contributors: johnjamesjacoby
9 | Tags: user, signups, admin, multisite
10 | Requires PHP: 7.2
11 | Requires at least: 5.2
12 | Tested up to: 5.8
13 | Stable tag: 5.0.2
14 |
15 | == Description ==
16 |
17 | WP User Signups is the best way to manage user & site sign-ups in WordPress.
18 |
19 | Use it to:
20 |
21 | * View & manage user sign-ups the WordPress way
22 | * Resend activation emails to users who haven't received theirs yet
23 | * Manually edit & activate sign-ups for users who are having trouble
24 |
25 | = Recommended Plugins =
26 |
27 | If you like this plugin, you'll probably like these!
28 |
29 | * [WP User Profiles](https://wordpress.org/plugins/wp-user-profiles/ "A sophisticated way to edit users in WordPress.")
30 | * [WP User Activity](https://wordpress.org/plugins/wp-user-activity/ "The best way to log activity in WordPress.")
31 | * [WP User Avatars](https://wordpress.org/plugins/wp-user-avatars/ "Allow users to upload avatars or choose them from your media library.")
32 | * [WP User Groups](https://wordpress.org/plugins/wp-user-groups/ "Group users together with taxonomies & terms.")
33 | * [WP User Signups](https://wordpress.org/plugins/wp-user-signups/ "The best way to manage user & site sign-ups in WordPress.")
34 | * [WP Term Authors](https://wordpress.org/plugins/wp-term-authors/ "Authors for categories, tags, and other taxonomy terms.")
35 | * [WP Term Colors](https://wordpress.org/plugins/wp-term-colors/ "Pretty colors for categories, tags, and other taxonomy terms.")
36 | * [WP Term Families](https://wordpress.org/plugins/wp-term-families/ "Associate taxonomy terms with other taxonomy terms.")
37 | * [WP Term Icons](https://wordpress.org/plugins/wp-term-icons/ "Pretty icons for categories, tags, and other taxonomy terms.")
38 | * [WP Term Images](https://wordpress.org/plugins/wp-term-images/ "Pretty images for categories, tags, and other taxonomy terms.")
39 | * [WP Term Locks](https://wordpress.org/plugins/wp-term-locks/ "Protect categories, tags, and other taxonomy terms from being edited or deleted.")
40 | * [WP Term Order](https://wordpress.org/plugins/wp-term-order/ "Sort taxonomy terms, your way.")
41 | * [WP Term Visibility](https://wordpress.org/plugins/wp-term-visibility/ "Visibilities for categories, tags, and other taxonomy terms.")
42 | * [WP Media Categories](https://wordpress.org/plugins/wp-media-categories/ "Add categories to media & attachments.")
43 | * [WP Pretty Filters](https://wordpress.org/plugins/wp-pretty-filters/ "Makes post filters better match what's already in Media & Attachments.")
44 | * [WP Chosen](https://wordpress.org/plugins/wp-chosen/ "Make long, unwieldy select boxes much more user-friendly.")
45 |
46 | = Credits =
47 |
48 | This plugin is largely inspired by:
49 |
50 | * BuddyPress
51 | * Unconfirmed
52 |
53 | == Screenshots ==
54 |
55 | 1. List Tables
56 | 2. Add New/Edit
57 |
58 | == Installation ==
59 |
60 | * Download and install using the built in WordPress plugin installer.
61 | * Activate in the "Plugins" area of your network admin by clicking the "Activate" link.
62 | * No further setup or configuration is necessary.
63 |
64 | == Frequently Asked Questions ==
65 |
66 | = Does this work with on single-site, multi-site, and multi-network installations? =
67 |
68 | Yes. Yes. Yes.
69 |
70 | = Does this work with BuddyPress, bbPress, and GlotPress? =
71 |
72 | Yes. Yes. Yes.
73 |
74 | = Does this work with other membership plugins? =
75 |
76 | I'm not really sure! Please test it with your favorite ones and let me know!
77 |
78 | = Where can I get support? =
79 |
80 | * Community: https://wordpress.org/support/plugin/wp-user-signups
81 | * Development: https://github.com/stuttter/wp-user-signups/discussions
82 |
83 | == Changelog ==
84 |
85 | = [5.0.2] - 2021-05-29 =
86 | * Update author info
87 | * Add sponsor link
88 |
89 | = [5.0.1] - 2021-03-21 =
90 | * Improved PHP 7.4 support
91 |
92 | = [5.0.0] - 2020-05-05 =
93 | * PHP 7.3 support
94 | * Rename text domain back to match WordPress.org plugin slug
95 |
96 | = [4.0.0] - 2017-04-11 =
97 | * Database schema update
98 |
99 | = [3.1.0] - 2017-04-03 =
100 | * Signup meta support
101 |
102 | = [3.0.0] - 2017-04-03 =
103 | * Rebrand internally to WP Signups
104 |
105 | = [2.0.0] - 2017-03-15 =
106 | * Extend WP_DB_Table class
107 |
108 | = [1.2.0] - 2017-03-03 =
109 | * Fix bug with site column output
110 | * Single-site implementation improvements
111 | * Add descriptions to a few form fields
112 |
113 | = [1.1.0] - 2016-11-18 =
114 | * Caching improvements
115 |
116 | = [1.0.0] - 2016-11-13 =
117 | * Initial release
118 |
--------------------------------------------------------------------------------
/wp-user-signups.php:
--------------------------------------------------------------------------------
1 | schema = "ID bigint(20) NOT NULL auto_increment,
41 | email varchar(255) NOT NULL default '',
42 | IP varchar(30) NOT NULL default '',
43 | blog_id bigint(20) NOT NULL default '0',
44 | date_registered datetime NOT NULL default '0000-00-00 00:00:00',
45 | PRIMARY KEY (ID),
46 | KEY IP (IP)";
47 | }
48 |
49 | /**
50 | * Handle schema changes
51 | *
52 | * @since 2.0.0
53 | */
54 | protected function upgrade() {
55 |
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/wp-user-signups/includes/classes/class-wp-db-table-signupmeta.php:
--------------------------------------------------------------------------------
1 | schema = "meta_id bigint(20) unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
42 | signup_id bigint(20) unsigned NOT NULL default '0',
43 | meta_key varchar(255) DEFAULT NULL,
44 | meta_value longtext DEFAULT NULL,
45 | KEY signup_id (signup_id),
46 | KEY meta_key (meta_key({$max_index_length}))";
47 | }
48 |
49 | /**
50 | * Handle schema changes
51 | *
52 | * @since 1.0.0
53 | */
54 | protected function upgrade() {
55 |
56 | // 3.0.0 to 4.0.0
57 | if ( version_compare( (int) $this->db_version, 201704110001, '<=' ) ) {
58 | $this->db->query( "ALTER TABLE {$this->table_name} MODIFY `meta_id` BIGINT(20) unsigned NOT NULL AUTO_INCREMENT;" );
59 | $this->db->query( "ALTER TABLE {$this->table_name} MODIFY `signup_id` BIGINT(20) unsigned NOT NULL default 0;" );
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/wp-user-signups/includes/classes/class-wp-db-table-signups.php:
--------------------------------------------------------------------------------
1 | schema = "signup_id bigint(20) unsigned NOT NULL auto_increment,
41 | domain varchar(200) NOT NULL default '',
42 | path varchar(100) NOT NULL default '',
43 | title longtext NOT NULL,
44 | user_login varchar(60) NOT NULL default '',
45 | user_email varchar(100) NOT NULL default '',
46 | registered datetime NOT NULL default '0000-00-00 00:00:00',
47 | activated datetime NOT NULL default '0000-00-00 00:00:00',
48 | active tinyint(1) NOT NULL default '0',
49 | activation_key varchar(50) NOT NULL default '',
50 | meta longtext,
51 | PRIMARY KEY (signup_id),
52 | KEY activation_key (activation_key),
53 | KEY user_email (user_email),
54 | KEY user_login_email (user_login,user_email),
55 | KEY domain_path (domain(140),path(51))";
56 | }
57 |
58 | /**
59 | * Handle schema changes
60 | *
61 | * @since 2.0.0
62 | */
63 | protected function upgrade() {
64 |
65 | // 3.0.0 to 4.0.0
66 | if ( version_compare( (int) $this->db_version, 201704110001, '<=' ) ) {
67 | $this->db->query( "ALTER TABLE {$this->table_name} MODIFY `signup_id` BIGINT(20) unsigned NOT NULL AUTO_INCREMENT;" );
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/wp-user-signups/includes/classes/class-wp-db-table.php:
--------------------------------------------------------------------------------
1 | name ) ) {
91 | return;
92 | }
93 |
94 | // Setup the database
95 | $this->set_db();
96 |
97 | // Get the version of he table currently in the database
98 | $this->get_db_version();
99 |
100 | // Add the table to the object
101 | $this->set_wpdb_tables();
102 |
103 | // Setup the database schema
104 | $this->set_schema();
105 |
106 | // Add hooks to WordPress actions
107 | $this->add_hooks();
108 | }
109 |
110 | /** Abstract **************************************************************/
111 |
112 | /**
113 | * Setup this database table
114 | *
115 | * @since 1.0.0
116 | */
117 | protected abstract function set_schema();
118 |
119 | /**
120 | * Upgrade this database table
121 | *
122 | * @since 1.0.0
123 | */
124 | protected abstract function upgrade();
125 |
126 | /** Public ****************************************************************/
127 |
128 | /**
129 | * Update table version & references.
130 | *
131 | * Hooked to the "switch_blog" action.
132 | *
133 | * @since 1.0.0
134 | *
135 | * @param int $site_id
136 | */
137 | public function switch_blog( $site_id = 0 ) {
138 |
139 | // Update DB version based on the current site
140 | if ( false === $this->global ) {
141 | $this->db_version = get_blog_option( $site_id, $this->db_version_key, false );
142 | }
143 |
144 | // Update table references based on th current site
145 | $this->set_wpdb_tables();
146 | }
147 |
148 | /**
149 | * Maybe upgrade the database table. Handles creation & schema changes.
150 | *
151 | * Hooked to the "admin_init" action.
152 | *
153 | * @since 1.0.0
154 | */
155 | public function maybe_upgrade() {
156 |
157 | // Bail if no upgrade needed
158 | if ( version_compare( (int) $this->db_version, (int) $this->version, '>=' ) ) {
159 | return;
160 | }
161 |
162 | // Include file with dbDelta() for create/upgrade usages
163 | if ( ! function_exists( 'dbDelta' ) ) {
164 | require_once ABSPATH . 'wp-admin/includes/upgrade.php';
165 | }
166 |
167 | // Bail if global and upgrading global tables is not allowed
168 | if ( ( true === $this->global ) && ! wp_should_upgrade_global_tables() ) {
169 | return;
170 | }
171 |
172 | // Create or upgrade?
173 | $this->exists()
174 | ? $this->upgrade()
175 | : $this->create();
176 |
177 | // Set the database version
178 | if ( $this->exists() ) {
179 | $this->set_db_version();
180 | }
181 | }
182 |
183 | /** Private ***************************************************************/
184 |
185 | /**
186 | * Setup the necessary WPDB variables
187 | *
188 | * @since 1.0.0
189 | */
190 | private function set_db() {
191 |
192 | // Setup database
193 | $this->db = $GLOBALS['wpdb'];
194 | $this->name = sanitize_key( $this->name );
195 |
196 | // Maybe create database key
197 | if ( empty( $this->db_version_key ) ) {
198 | $this->db_version_key = "wpdb_{$this->name}_version";
199 | }
200 | }
201 |
202 | /**
203 | * Modify the database object and add the table to it
204 | *
205 | * This is necessary to do directly because WordPress does have a mechanism
206 | * for manipulating them safely. It's pretty fragile, but oh well.
207 | *
208 | * @since 1.0.0
209 | */
210 | private function set_wpdb_tables() {
211 |
212 | // Global
213 | if ( true === $this->global ) {
214 | $prefix = $this->db->get_blog_prefix( 0 );
215 | $this->db->{$this->name} = "{$prefix}{$this->name}";
216 | $this->db->ms_global_tables[] = $this->name;
217 |
218 | // Site
219 | } else {
220 | $prefix = $this->db->get_blog_prefix( null );
221 | $this->db->{$this->name} = "{$prefix}{$this->name}";
222 | $this->db->tables[] = $this->name;
223 | }
224 |
225 | // Set the table name locally
226 | $this->table_name = $this->db->{$this->name};
227 |
228 | // Charset
229 | if ( ! empty( $this->db->charset ) ) {
230 | $this->charset_collation = "DEFAULT CHARACTER SET {$this->db->charset}";
231 | }
232 |
233 | // Collation
234 | if ( ! empty( $this->db->collate ) ) {
235 | $this->charset_collation .= " COLLATE {$this->db->collate}";
236 | }
237 | }
238 |
239 | /**
240 | * Set the database version to the table version.
241 | *
242 | * Saves global table version to "wp_sitemeta" to the main network
243 | *
244 | * @since 1.0.0
245 | */
246 | private function set_db_version() {
247 |
248 | // Set the class version
249 | $this->db_version = $this->version;
250 |
251 | // Update the DB version
252 | ( true === $this->global )
253 | ? update_network_option( null, $this->db_version_key, $this->version )
254 | : update_option( $this->db_version_key, $this->version );
255 | }
256 |
257 | /**
258 | * Get the table version from the database.
259 | *
260 | * Gets global table version from "wp_sitemeta" to the main network
261 | *
262 | * @since 1.0.0
263 | */
264 | private function get_db_version() {
265 | $this->db_version = ( true === $this->global )
266 | ? get_network_option( null, $this->db_version_key, false )
267 | : get_option( $this->db_version_key, false );
268 | }
269 |
270 | /**
271 | * Add class hooks to WordPress actions
272 | *
273 | * @since 1.0.0
274 | */
275 | private function add_hooks() {
276 |
277 | // Activation hook
278 | register_activation_hook( __FILE__, array( $this, 'maybe_upgrade' ) );
279 |
280 | // Add table to the global database object
281 | add_action( 'switch_blog', array( $this, 'switch_blog' ) );
282 | add_action( 'admin_init', array( $this, 'maybe_upgrade' ) );
283 | }
284 |
285 | /**
286 | * Create the table
287 | *
288 | * @since 1.0.0
289 | */
290 | private function create() {
291 |
292 | // Run CREATE TABLE query
293 | $created = dbDelta( array( "CREATE TABLE {$this->table_name} ( {$this->schema} ) {$this->charset_collation};" ) );
294 |
295 | // Was anything created?
296 | return ! empty( $created );
297 | }
298 |
299 | /**
300 | * Check if table already exists
301 | *
302 | * @since 1.0.0
303 | *
304 | * @return bool
305 | */
306 | private function exists() {
307 | $query = "SHOW TABLES LIKE %s";
308 | $like = $this->db->esc_like( $this->table_name );
309 | $prepared = $this->db->prepare( $query, $like );
310 | $table_exist = $this->db->get_var( $prepared );
311 |
312 | return ! empty( $table_exist );
313 | }
314 | }
315 | endif;
316 |
--------------------------------------------------------------------------------
/wp-user-signups/includes/classes/class-wp-signup-query.php:
--------------------------------------------------------------------------------
1 | '',
36 | 'from' => '',
37 | 'where' => array(),
38 | 'groupby' => '',
39 | 'orderby' => '',
40 | 'limits' => '',
41 | );
42 |
43 | /**
44 | * Registered query container.
45 | *
46 | * @since 1.0.0
47 | * @access public
48 | * @var object WP_Date_Query
49 | */
50 | public $registered_query = false;
51 |
52 | /**
53 | * Activated query container.
54 | *
55 | * @since 1.0.0
56 | * @access public
57 | * @var object WP_Date_Query
58 | */
59 | public $activated_query = false;
60 |
61 | /**
62 | * Meta query container.
63 | *
64 | * @since 1.0.0
65 | * @access public
66 | * @var object WP_Date_Query
67 | */
68 | public $meta_query = false;
69 |
70 | /**
71 | * Query vars set by the user.
72 | *
73 | * @since 1.0.0
74 | * @access public
75 | * @var array
76 | */
77 | public $query_vars;
78 |
79 | /**
80 | * Default values for query vars.
81 | *
82 | * @since 1.0.0
83 | * @access public
84 | * @var array
85 | */
86 | public $query_var_defaults;
87 |
88 | /**
89 | * List of signups located by the query.
90 | *
91 | * @since 1.0.0
92 | * @access public
93 | * @var array
94 | */
95 | public $signups;
96 |
97 | /**
98 | * The amount of found signups for the current query.
99 | *
100 | * @since 1.0.0
101 | * @access public
102 | * @var int
103 | */
104 | public $found_signups = 0;
105 |
106 | /**
107 | * The number of pages.
108 | *
109 | * @since 1.0.0
110 | * @access public
111 | * @var int
112 | */
113 | public $max_num_pages = 0;
114 |
115 | /**
116 | * The database object
117 | *
118 | * @since 1.0.0
119 | *
120 | * @var WPDB
121 | */
122 | private $db;
123 |
124 | /**
125 | * Sets up the signup query, based on the query vars passed.
126 | *
127 | * @since 1.0.0
128 | * @access public
129 | *
130 | * @param string|array $query {
131 | * Optional. Array or query string of signup query parameters. Default empty.
132 | *
133 | * @type int $ID An signup ID to only return that signup. Default empty.
134 | * @type array $signup__in Array of signup IDs to include. Default empty.
135 | * @type array $signup__not_in Array of signup IDs to exclude. Default empty.
136 | * @type string $domain Limit results to those affiliated with a given domain.
137 | * Default empty.
138 | * @type array $domain__in Array of domains to include affiliated signups for. Default empty.
139 | * @type array $domain__not_in Array of domains to exclude affiliated signups for. Default empty.
140 | * @type string $path Limit results to those affiliated with a given path.
141 | * Default empty.
142 | * @type array $path__in Array of paths to include affiliated signups for. Default empty.
143 | * @type array $path__not_in Array of paths to exclude affiliated signups for. Default empty.
144 | * @type string $user_login Limit results to those affiliated with a given login.
145 | * Default empty.
146 | * @type array $user_login__in Array of logins to include affiliated signups for. Default empty.
147 | * @type array $user_login__not_in Array of logins to exclude affiliated signups for. Default empty.
148 | * @type string $user_email Limit results to those affiliated with a given email.
149 | * Default empty.
150 | * @type array $user_email__in Array of emails to include affiliated signups for. Default empty.
151 | * @type array $user_email__not_in Array of emails to exclude affiliated signups for. Default empty.
152 | * @type array $registered_query Date query clauses to limit signups by. See WP_Date_Query.
153 | * Default null.
154 | * @type array $activated_query Date query clauses to limit signups by. See WP_Date_Query.
155 | * Default null.
156 | * @type int $active Limit results to those affiliated with a given path.
157 | * @type string $key Limit results to those affiliated with a given key.
158 | * Default empty.
159 | * @type array $key__in Array of keys to include affiliated signups for. Default empty.
160 | * @type array $key__not_in Array of keys to exclude affiliated signups for. Default empty.
161 | * @type string $fields Site fields to return. Accepts 'ids' (returns an array of signup IDs)
162 | * or empty (returns an array of complete signup objects). Default empty.
163 | * @type bool $count Whether to return a signup count (true) or array of signup objects.
164 | * Default false.
165 | * @type int $number Maximum number of signups to retrieve. Default null (no limit).
166 | * @type int $offset Number of signups to offset the query. Used to build LIMIT clause.
167 | * Default 0.
168 | * @type bool $no_found_rows Whether to disable the `SQL_CALC_FOUND_ROWS` query. Default true.
169 | * @type string|array $orderby Site status or array of statuses. Accepts 'id', 'registered', 'activated',
170 | * 'domain_length', 'path_length', 'login', or 'email. Also accepts false,
171 | * an empty array, or 'none' to disable `ORDER BY` clause.
172 | * Default 'id'.
173 | * @type string $order How to order retrieved signups. Accepts 'ASC', 'DESC'. Default 'ASC'.
174 | * @type string $search Search term(s) to retrieve matching signups for. Default empty.
175 | * @type array $search_columns Array of column names to be searched. Accepts 'domain' and 'status'.
176 | * Default empty array.
177 | *
178 | * @type bool $update_signup_cache Whether to prime the cache for found signups. Default false.
179 | * }
180 | */
181 | public function __construct( $query = '' ) {
182 | $this->db = $GLOBALS['wpdb'];
183 | $this->query_var_defaults = array(
184 | 'fields' => '',
185 | 'ID' => '',
186 | 'signup__in' => '',
187 | 'signup__not_in' => '',
188 | 'domain' => '',
189 | 'domain__in' => '',
190 | 'domain__not_in' => '',
191 | 'path' => '',
192 | 'path__in' => '',
193 | 'path__not_in' => '',
194 | 'title' => '',
195 | 'user_login' => '',
196 | 'user_login__in' => '',
197 | 'user_login__not_in' => '',
198 | 'user_email' => '',
199 | 'user_email__in' => '',
200 | 'user_email__not_in' => '',
201 | 'active' => 0,
202 | 'key' => '',
203 | 'key__in' => '',
204 | 'key__not_in' => '',
205 | 'number' => 100,
206 | 'offset' => '',
207 | 'orderby' => 'signup_id',
208 | 'order' => 'ASC',
209 | 'search' => '',
210 | 'search_columns' => array(),
211 | 'count' => false,
212 | 'registered_query' => null, // See WP_Date_Query
213 | 'activated_query' => null, // See WP_Date_Query
214 | 'meta_query' => null, // See WP_Meta_Query
215 | 'no_found_rows' => true,
216 | 'update_signup_cache' => true,
217 | 'update_signup_meta_cache' => true,
218 | );
219 |
220 | if ( ! empty( $query ) ) {
221 | $this->query( $query );
222 | }
223 | }
224 |
225 | /**
226 | * Parses arguments passed to the signup query with default query parameters.
227 | *
228 | * @since 1.0.0
229 | * @access public
230 | *
231 | * @see WP_Signup_Query::__construct()
232 | *
233 | * @param string|array $query Array or string of WP_Signup_Query arguments. See WP_Signup_Query::__construct().
234 | */
235 | public function parse_query( $query = '' ) {
236 | if ( empty( $query ) ) {
237 | $query = $this->query_vars;
238 | }
239 |
240 | $this->query_vars = wp_parse_args( $query, $this->query_var_defaults );
241 |
242 | /**
243 | * Fires after the signup query vars have been parsed.
244 | *
245 | * @since 1.0.0
246 | *
247 | * @param WP_Signup_Query &$this The WP_Signup_Query instance (passed by reference).
248 | */
249 | do_action_ref_array( 'parse_signups_query', array( &$this ) );
250 | }
251 |
252 | /**
253 | * Sets up the WordPress query for retrieving signups.
254 | *
255 | * @since 1.0.0
256 | * @access public
257 | *
258 | * @param string|array $query Array or URL query string of parameters.
259 | * @return array|int List of signups, or number of signups when 'count' is passed as a query var.
260 | */
261 | public function query( $query ) {
262 | $this->query_vars = wp_parse_args( $query );
263 |
264 | return $this->get_signups();
265 | }
266 |
267 | /**
268 | * Retrieves a list of signups matching the query vars.
269 | *
270 | * @since 1.0.0
271 | * @access public
272 | *
273 | * @return array|int List of signups, or number of signups when 'count' is passed as a query var.
274 | */
275 | public function get_signups() {
276 | $this->parse_query();
277 |
278 | /**
279 | * Fires before signups are retrieved.
280 | *
281 | * @since 1.0.0
282 | *
283 | * @param WP_Signup_Query &$this Current instance of WP_Signup_Query, passed by reference.
284 | */
285 | do_action_ref_array( 'pre_get_signups', array( &$this ) );
286 |
287 | // $args can include anything. Only use the args defined in the query_var_defaults to compute the key.
288 | $key = md5( serialize( wp_array_slice_assoc( $this->query_vars, array_keys( $this->query_var_defaults ) ) ) );
289 | $last_changed = wp_cache_get_last_changed( 'signups' );
290 |
291 | // Check the cache
292 | $cache_key = "get_signups:{$key}:{$last_changed}";
293 | $cache_value = wp_cache_get( $cache_key, 'signups' );
294 |
295 | // No cache value
296 | if ( false === $cache_value ) {
297 | $signup_ids = $this->get_signup_ids();
298 |
299 | // Set the number of found signups (make sure it's not a count)
300 | if ( ! empty( $signup_ids ) && is_array( $signup_ids ) ) {
301 | $this->set_found_signups( $signup_ids );
302 | }
303 |
304 | // Format the cached value
305 | $cache_value = array(
306 | 'signup_ids' => $signup_ids,
307 | 'found_signups' => intval( $this->found_signups ),
308 | );
309 |
310 | // Add value to the cache
311 | wp_cache_add( $cache_key, $cache_value, 'signups' );
312 |
313 | // Value exists in cache
314 | } else {
315 | $signup_ids = $cache_value['signup_ids'];
316 | $this->found_signups = intval( $cache_value['found_signups'] );
317 | }
318 |
319 | // Pagination
320 | if ( $this->found_signups && $this->query_vars['number'] ) {
321 | $this->max_num_pages = ceil( $this->found_signups / $this->query_vars['number'] );
322 | }
323 |
324 | // Return an int of the count
325 | if ( $this->query_vars['count'] ) {
326 | return intval( $signup_ids );
327 | }
328 |
329 | // Cast to integers
330 | $signup_ids = array_map( 'intval', $signup_ids );
331 |
332 | // Prime signup caches.
333 | if ( $this->query_vars['update_signup_cache'] ) {
334 | _prime_signup_caches( $signup_ids, $this->query_vars['update_signup_meta_cache'] );
335 | }
336 |
337 | // Return the IDs
338 | if ( 'ids' === $this->query_vars['fields'] ) {
339 | $this->signups = $signup_ids;
340 |
341 | return $this->signups;
342 | }
343 |
344 | // Get signup instances from IDs.
345 | $_signups = array_map( 'get_signup', $signup_ids );
346 |
347 | /**
348 | * Filters the site query results.
349 | *
350 | * @since 1.0.0
351 | *
352 | * @param array $results An array of signups.
353 | * @param WP_Signup_Query &$this Current instance of WP_Signup_Query, passed by reference.
354 | */
355 | $_signups = apply_filters_ref_array( 'the_signups', array( $_signups, &$this ) );
356 |
357 | // Make sure signups are still signup instances.
358 | $this->signups = array_map( 'get_signup', $_signups );
359 |
360 | return $this->signups;
361 | }
362 |
363 | /**
364 | * Used internally to get a list of signup IDs matching the query vars.
365 | *
366 | * @since 1.0.0
367 | * @access protected
368 | *
369 | * @return int|array A single count of signup IDs if a count query. An array of signup IDs if a full query.
370 | */
371 | protected function get_signup_ids() {
372 | $order = $this->parse_order( $this->query_vars['order'] );
373 |
374 | // Disable ORDER BY with 'none', an empty array, or boolean false.
375 | if ( in_array( $this->query_vars['orderby'], array( 'none', array(), false ), true ) ) {
376 | $orderby = '';
377 | } elseif ( ! empty( $this->query_vars['orderby'] ) ) {
378 | $ordersby = is_array( $this->query_vars['orderby'] ) ?
379 | $this->query_vars['orderby'] :
380 | preg_split( '/[,\s]/', $this->query_vars['orderby'] );
381 |
382 | $orderby_array = array();
383 | foreach ( $ordersby as $_key => $_value ) {
384 | if ( ! $_value ) {
385 | continue;
386 | }
387 |
388 | if ( is_int( $_key ) ) {
389 | $_orderby = $_value;
390 | $_order = $order;
391 | } else {
392 | $_orderby = $_key;
393 | $_order = $_value;
394 | }
395 |
396 | $parsed = $this->parse_orderby( $_orderby );
397 |
398 | if ( empty( $parsed ) ) {
399 | continue;
400 | }
401 |
402 | if ( 'signup__in' === $_orderby ) {
403 | $orderby_array[] = $parsed;
404 | continue;
405 | }
406 |
407 | $orderby_array[] = $parsed . ' ' . $this->parse_order( $_order );
408 | }
409 |
410 | $orderby = implode( ', ', $orderby_array );
411 | } else {
412 | $orderby = "us.signup_id {$order}";
413 | }
414 |
415 | $number = absint( $this->query_vars['number'] );
416 | $offset = absint( $this->query_vars['offset'] );
417 |
418 | if ( ! empty( $number ) ) {
419 | if ( $offset ) {
420 | $limits = 'LIMIT ' . $offset . ',' . $number;
421 | } else {
422 | $limits = 'LIMIT ' . $number;
423 | }
424 | }
425 |
426 | if ( $this->query_vars['count'] ) {
427 | $fields = 'COUNT(*)';
428 | } else {
429 | $fields = 'us.signup_id';
430 | }
431 |
432 | // Parse signup IDs for an IN clause.
433 | $signup_id = absint( $this->query_vars['ID'] );
434 | if ( ! empty( $signup_id ) ) {
435 | $this->sql_clauses['where']['ID'] = $this->db->prepare( 'us.signup_id = %d', $signup_id );
436 | }
437 |
438 | // Parse signup IDs for an IN clause.
439 | if ( ! empty( $this->query_vars['signup__in'] ) ) {
440 | $this->sql_clauses['where']['signup__in'] = "us.signup_id IN ( " . implode( ',', wp_parse_id_list( $this->query_vars['site__in'] ) ) . ' )';
441 | }
442 |
443 | // Parse signup IDs for a NOT IN clause.
444 | if ( ! empty( $this->query_vars['signup__not_in'] ) ) {
445 | $this->sql_clauses['where']['signup__not_in'] = "us.signup_id NOT IN ( " . implode( ',', wp_parse_id_list( $this->query_vars['site__not_in'] ) ) . ' )';
446 | }
447 |
448 | // domain
449 | if ( ! empty( $this->query_vars['domain'] ) ) {
450 | $this->sql_clauses['where']['domain'] = $this->db->prepare( 'us.domain = %s', $this->query_vars['domain'] );
451 | }
452 |
453 | // Parse signup domain for an IN clause.
454 | if ( is_array( $this->query_vars['domain__in'] ) ) {
455 | $this->sql_clauses['where']['domain__in'] = "us.domain IN ( '" . implode( "', '", $this->db->_escape( $this->query_vars['domain__in'] ) ) . "' )";
456 | }
457 |
458 | // Parse signup domain for a NOT IN clause.
459 | if ( is_array( $this->query_vars['domain__not_in'] ) ) {
460 | $this->sql_clauses['where']['domain__not_in'] = "us.domain NOT IN ( '" . implode( "', '", $this->db->_escape( $this->query_vars['domain__not_in'] ) ) . "' )";
461 | }
462 |
463 | // path
464 | if ( ! empty( $this->query_vars['path'] ) ) {
465 | $this->sql_clauses['where']['path'] = $this->db->prepare( 'us.path = %s', $this->query_vars['path'] );
466 | }
467 |
468 | // Parse signup path for an IN clause.
469 | if ( is_array( $this->query_vars['path__in'] ) ) {
470 | $this->sql_clauses['where']['path__in'] = "us.path IN ( '" . implode( "', '", $this->db->_escape( $this->query_vars['path__in'] ) ) . "' )";
471 | }
472 |
473 | // Parse signup path for a NOT IN clause.
474 | if ( is_array( $this->query_vars['path__not_in'] ) ) {
475 | $this->sql_clauses['where']['path__not_in'] = "us.path NOT IN ( '" . implode( "', '", $this->db->_escape( $this->query_vars['path__not_in'] ) ) . "' )";
476 | }
477 |
478 | // user_login
479 | if ( ! empty( $this->query_vars['user_login'] ) ) {
480 | $this->sql_clauses['where']['user_login'] = $this->db->prepare( 'us.user_login = %s', $this->query_vars['user_login'] );
481 | }
482 |
483 | // Parse signup user_login for an IN clause.
484 | if ( is_array( $this->query_vars['user_login__in'] ) ) {
485 | $this->sql_clauses['where']['user_login__in'] = "us.user_login IN ( '" . implode( "', '", $this->db->_escape( $this->query_vars['user_login__in'] ) ) . "' )";
486 | }
487 |
488 | // Parse signup user_login for a NOT IN clause.
489 | if ( is_array( $this->query_vars['user_login__not_in'] ) ) {
490 | $this->sql_clauses['where']['user_login__not_in'] = "us.user_login NOT IN ( '" . implode( "', '", $this->db->_escape( $this->query_vars['user_login__not_in'] ) ) . "' )";
491 | }
492 |
493 | // user_email
494 | if ( ! empty( $this->query_vars['user_email'] ) ) {
495 | $this->sql_clauses['where']['user_email'] = $this->db->prepare( 'us.user_email = %s', $this->query_vars['user_email'] );
496 | }
497 |
498 | // Parse signup user_email for an IN clause.
499 | if ( is_array( $this->query_vars['user_email__in'] ) ) {
500 | $this->sql_clauses['where']['user_email__in'] = "us.user_email IN ( '" . implode( "', '", $this->db->_escape( $this->query_vars['user_email__in'] ) ) . "' )";
501 | }
502 |
503 | // Parse signup user_email for a NOT IN clause.
504 | if ( is_array( $this->query_vars['user_email__not_in'] ) ) {
505 | $this->sql_clauses['where']['user_email__not_in'] = "us.user_email NOT IN ( '" . implode( "', '", $this->db->_escape( $this->query_vars['user_email__not_in'] ) ) . "' )";
506 | }
507 |
508 | if ( isset( $this->query_vars['active'] ) ) {
509 | $this->sql_clauses['where']['active'] = $this->db->prepare( 'us.active = %d', (int) $this->query_vars['active'] );
510 | }
511 |
512 | // Falsey search strings are ignored.
513 | if ( strlen( $this->query_vars['search'] ) ) {
514 | $search_columns = array();
515 |
516 | if ( $this->query_vars['search_columns'] ) {
517 | $search_columns = array_intersect( $this->query_vars['search_columns'], array( 'domain', 'path', 'title', 'user_login', 'user_email', 'activation_key' ) );
518 | }
519 |
520 | if ( empty( $search_columns ) ) {
521 | $search_columns = array( 'domain', 'path', 'title', 'user_login', 'user_email', 'activation_key' );
522 | }
523 |
524 | /**
525 | * Filters the columns to search in a WP_Signup_Query search.
526 | *
527 | * The default columns include 'domain' and 'path.
528 | *
529 | * @since 1.0.0
530 | *
531 | * @param array $search_columns Array of column names to be searched.
532 | * @param string $search Text being searched.
533 | * @param WP_Signup_Query $this The current WP_Signup_Query instance.
534 | */
535 | $search_columns = apply_filters( 'signup_search_columns', $search_columns, $this->query_vars['search'], $this );
536 |
537 | $this->sql_clauses['where']['search'] = $this->get_search_sql( $this->query_vars['search'], $search_columns );
538 | }
539 |
540 | $registered_query = $this->query_vars['registered_query'];
541 | if ( ! empty( $registered_query ) && is_array( $registered_query ) ) {
542 | $this->registered_query = new WP_Date_Query( $registered_query, 'us.registered' );
543 | $this->sql_clauses['where']['registered_query'] = preg_replace( '/^\s*AND\s*/', '', $this->registered_query->get_sql() );
544 | }
545 |
546 | $activated_query = $this->query_vars['activated_query'];
547 | if ( ! empty( $activated_query) && is_array( $activated_query) ) {
548 | $this->activated_query = new WP_Date_Query( $activated_query, 'us.activated' );
549 | $this->sql_clauses['where']['activated_query'] = preg_replace( '/^\s*AND\s*/', '', $this->activated_query->get_sql() );
550 | }
551 |
552 | $meta_query = $this->query_vars['meta_query'];
553 | if ( ! empty( $meta_query ) && is_array( $meta_query ) ) {
554 | $this->meta_query = new WP_Meta_Query( $meta_query );
555 | $clauses = $this->meta_query->get_sql( 'blog_signup', 'us', 'id', $this );
556 | $join = $clauses['join'];
557 | $this->sql_clauses['where']['meta_query'] = preg_replace( '/^\s*AND\s*/', '', $clauses['where'] );
558 | } else {
559 | $join = '';
560 | }
561 |
562 | $where = implode( ' AND ', $this->sql_clauses['where'] );
563 |
564 | // Not currently used. Set to empty to prevent debug notices.
565 | $groupby = '';
566 |
567 | $pieces = array( 'fields', 'join', 'where', 'orderby', 'limits', 'groupby' );
568 |
569 | /**
570 | * Filters the signup query clauses.
571 | *
572 | * @since 1.0.0
573 | *
574 | * @param array $pieces A compacted array of signup query clauses.
575 | * @param WP_Signup_Query &$this Current instance of WP_Signup_Query, passed by reference.
576 | */
577 | $clauses = apply_filters_ref_array( 'signup_clauses', array( compact( $pieces ), &$this ) );
578 |
579 | $fields = isset( $clauses['fields'] ) ? $clauses['fields'] : '';
580 | $join = isset( $clauses['join'] ) ? $clauses['join'] : '';
581 | $where = isset( $clauses['where'] ) ? $clauses['where'] : '';
582 | $orderby = isset( $clauses['orderby'] ) ? $clauses['orderby'] : '';
583 | $limits = isset( $clauses['limits'] ) ? $clauses['limits'] : '';
584 | $groupby = isset( $clauses['groupby'] ) ? $clauses['groupby'] : '';
585 |
586 | if ( $where ) {
587 | $where = "WHERE {$where}";
588 | }
589 |
590 | if ( $groupby ) {
591 | $groupby = "GROUP BY {$groupby}";
592 | }
593 |
594 | if ( $orderby ) {
595 | $orderby = "ORDER BY {$orderby}";
596 | }
597 |
598 | $found_rows = '';
599 | if ( ! $this->query_vars['no_found_rows'] ) {
600 | $found_rows = 'SQL_CALC_FOUND_ROWS';
601 | }
602 |
603 | $this->sql_clauses['select'] = "SELECT {$found_rows} {$fields}";
604 | $this->sql_clauses['from'] = "FROM {$this->db->signups} us {$join}";
605 | $this->sql_clauses['groupby'] = $groupby;
606 | $this->sql_clauses['orderby'] = $orderby;
607 | $this->sql_clauses['limits'] = $limits;
608 |
609 | $this->request = "{$this->sql_clauses['select']} {$this->sql_clauses['from']} {$where} {$this->sql_clauses['groupby']} {$this->sql_clauses['orderby']} {$this->sql_clauses['limits']}";
610 |
611 | if ( $this->query_vars['count'] ) {
612 | return intval( $this->db->get_var( $this->request ) );
613 | }
614 |
615 | $signup_ids = $this->db->get_col( $this->request );
616 |
617 | return array_map( 'intval', $signup_ids );
618 | }
619 |
620 | /**
621 | * Populates found_signups and max_num_pages properties for the current query
622 | * if the limit clause was used.
623 | *
624 | * @since 1.0.0
625 | * @access private
626 | *
627 | * @param array $signup_ids Optional array of signup IDs
628 | */
629 | private function set_found_signups( $signup_ids = array() ) {
630 |
631 | if ( ! empty( $this->query_vars['number'] ) && ! empty( $this->query_vars['no_found_rows'] ) ) {
632 | /**
633 | * Filters the query used to retrieve found signup count.
634 | *
635 | * @since 1.0.0
636 | *
637 | * @param string $found_signups_query SQL query. Default 'SELECT FOUND_ROWS()'.
638 | * @param WP_Signup_Query $signup_query The `WP_Signup_Query` instance.
639 | */
640 | $found_signups_query = apply_filters( 'found_signups_query', 'SELECT FOUND_ROWS()', $this );
641 |
642 | $this->found_signups = (int) $this->db->get_var( $found_signups_query );
643 | } elseif ( ! empty( $signup_ids ) ) {
644 | $this->found_signups = count( $signup_ids );
645 | }
646 | }
647 |
648 | /**
649 | * Used internally to generate an SQL string for searching across multiple columns.
650 | *
651 | * @since 1.0.0
652 | * @access protected
653 | *
654 | * @param string $string Search string.
655 | * @param array $columns Columns to search.
656 | * @return string Search SQL.
657 | */
658 | protected function get_search_sql( $string, $columns ) {
659 |
660 | if ( false !== strpos( $string, '*' ) ) {
661 | $like = '%' . implode( '%', array_map( array( $this->db, 'esc_like' ), explode( '*', $string ) ) ) . '%';
662 | } else {
663 | $like = '%' . $this->db->esc_like( $string ) . '%';
664 | }
665 |
666 | $searches = array();
667 | foreach ( $columns as $column ) {
668 | $searches[] = $this->db->prepare( "$column LIKE %s", $like );
669 | }
670 |
671 | return '(' . implode( ' OR ', $searches ) . ')';
672 | }
673 |
674 | /**
675 | * Parses and sanitizes 'orderby' keys passed to the signup query.
676 | *
677 | * @since 1.0.0
678 | * @access protected
679 | *
680 | * @param string $orderby Alias for the field to order by.
681 | * @return string|false Value to used in the ORDER clause. False otherwise.
682 | */
683 | protected function parse_orderby( $orderby ) {
684 |
685 | $parsed = false;
686 |
687 | switch ( $orderby ) {
688 | case 'id':
689 | case 'signup_id':
690 | $parsed = 'us.signup_id';
691 | break;
692 | case 'signup__in':
693 | $signup__in = implode( ',', array_map( 'absint', $this->query_vars['signup__in'] ) );
694 | $parsed = "FIELD( us.signup_id, {$signup__in} )";
695 | break;
696 | case 'domain':
697 | case 'path':
698 | case 'registered':
699 | case 'activated':
700 | case 'user_login':
701 | case 'user_email':
702 | $parsed = $orderby;
703 | break;
704 | case 'domain_length':
705 | $parsed = 'CHAR_LENGTH(domain)';
706 | break;
707 | }
708 |
709 | return $parsed;
710 | }
711 |
712 | /**
713 | * Parses an 'order' query variable and cast it to 'ASC' or 'DESC' as necessary.
714 | *
715 | * @since 1.0.0
716 | * @access protected
717 | *
718 | * @param string $order The 'order' query variable.
719 | * @return string The sanitized 'order' query variable.
720 | */
721 | protected function parse_order( $order ) {
722 | if ( ! is_string( $order ) || empty( $order ) ) {
723 | return 'ASC';
724 | }
725 |
726 | if ( 'ASC' === strtoupper( $order ) ) {
727 | return 'ASC';
728 | } else {
729 | return 'DESC';
730 | }
731 | }
732 | }
733 |
--------------------------------------------------------------------------------
/wp-user-signups/includes/classes/class-wp-signup.php:
--------------------------------------------------------------------------------
1 | data = $data;
35 | }
36 |
37 | /**
38 | * Clone magic method when clone( self ) is called.
39 | *
40 | * As the internal data is stored in an object, we have to make a copy
41 | * when this object is cloned.
42 | *
43 | * @since 1.0.0
44 | */
45 | public function __clone() {
46 | $this->data = clone( $this->data );
47 | }
48 |
49 | /**
50 | * Magic getter to retrieve from data array
51 | *
52 | * @since 1.0.0
53 | *
54 | * @param string $key
55 | * @return mixed Value if in data array. Null if not.
56 | */
57 | public function __get( $key ) {
58 | return isset( $this->data->{$key} )
59 | ? $this->data->{$key}
60 | : null;
61 | }
62 |
63 | /**
64 | * Update the signup
65 | *
66 | * See also, {@see set_domain} and {@see set_status} as convenience methods.
67 | *
68 | * @since 1.0.0
69 | *
70 | * @global WPDB $wpdb
71 | * @param array|stdClass $data Signup fields (associative array or object properties)
72 | *
73 | * @return bool|WP_Error True if we updated, false if we didn't need to, or WP_Error if an error occurred
74 | */
75 | public function update( $data = array() ) {
76 | global $wpdb;
77 |
78 | // Query
79 | $signup_id = (int) $this->signup_id;
80 | $where = array( 'signup_id' => $signup_id );
81 | $where_format = array( '%d' );
82 | $formats = array( '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%d', '%s', '%s' );
83 | $fields = self::validate( (array) $data );
84 | $result = $wpdb->update( $wpdb->signups, $fields, $where, $formats, $where_format );
85 |
86 | // Check for errors
87 | if ( empty( $result ) && ! empty( $wpdb->last_error ) ) {
88 | return new WP_Error( 'wp_signups_update_failed', esc_html__( 'An error has occurred.', 'wp-user-signups' ), $this );
89 | }
90 |
91 | // Clone object to pass into object later
92 | $old_signup = clone( $this );
93 |
94 | // Update internal state
95 | foreach ( $fields as $key => $val ) {
96 | $this->data->{$key} = $val;
97 | }
98 |
99 | // Clean item cache
100 | clean_signup_cache( $this );
101 |
102 | /**
103 | * Fires after a signup has been updated.
104 | *
105 | * @param WP_Signup $signup The signup object.
106 | * @param WP_Signup $signup The previous signup object.
107 | */
108 | do_action( 'wp_signups_updated', $this, $old_signup );
109 |
110 | return true;
111 | }
112 |
113 | /**
114 | * Delete the signup
115 | *
116 | * @since 1.0.0
117 | *
118 | * @return bool|WP_Error True if we updated, false if we didn't need to, or WP_Error if an error occurred
119 | */
120 | public function delete() {
121 | global $wpdb;
122 |
123 | // Delete
124 | $signup_id = (int) $this->signup_id;
125 | $where = array( 'signup_id' => $signup_id );
126 | $where_format = array( '%d' );
127 | $result = $wpdb->delete( $wpdb->signups, $where, $where_format );
128 |
129 | // Bail with error
130 | if ( empty( $result ) ) {
131 | return new WP_Error( 'wp_signups_delete_failed', esc_html__( 'Delete failed.', 'wp-user-signups' ), $this );
132 | }
133 |
134 | // Delete signup meta
135 | $signup_meta_ids = $wpdb->get_col( $wpdb->prepare( "SELECT meta_id FROM {$wpdb->signups} WHERE signup_id = %d", $signup_id ) );
136 | foreach ( $signup_meta_ids as $mid ) {
137 | delete_metadata_by_mid( 'signup', $mid );
138 | }
139 |
140 | // Delete cache
141 | clean_signup_cache( $this );
142 |
143 | /**
144 | * Fires after a signup has been deleted.
145 | *
146 | * @param WP_Signup $signup The signup object.
147 | */
148 | do_action( 'wp_signups_deleted', $this );
149 |
150 | return true;
151 | }
152 |
153 | /**
154 | * Get signup by signup ID
155 | *
156 | * @since 1.0.0
157 | *
158 | * @param int|WP_Signup $signup Signup ID or instance
159 | * @return WP_Signup Signup always, even if empty
160 | */
161 | public static function get_instance( $signup ) {
162 | global $wpdb;
163 |
164 | // Allow passing a site object in
165 | if ( $signup instanceof WP_Signup ) {
166 | return $signup;
167 | }
168 |
169 | if ( ! is_numeric( $signup ) ) {
170 | return new WP_Error( 'wp_signups_invalid_id', esc_html__( 'Signup not found.', 'wp-user-signups' ), $signup );
171 | }
172 |
173 | // Check cache first
174 | $_signup = wp_cache_get( $signup, 'signups' );
175 |
176 | // No cached alias
177 | if ( false === $_signup ) {
178 |
179 | // Suppress errors in case the table doesn't exist
180 | $suppress = $wpdb->suppress_errors();
181 | $_signup = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->signups} WHERE signup_id = %d", absint( $signup ) ) );
182 | $wpdb->suppress_errors( $suppress );
183 |
184 | // Add alias to cache
185 | if ( ! empty( $_signup ) && ! is_wp_error( $_signup ) ) {
186 | wp_cache_add( $signup, $_signup, 'signups' );
187 | } else {
188 | $_signup = array();
189 | }
190 | }
191 |
192 | // Signup exists
193 | return new WP_Signup( $_signup );
194 | }
195 |
196 | /**
197 | * Create a new signup
198 | *
199 | * @param array $args Array of signup details
200 | *
201 | * @return WP_Signup|WP_Error
202 | */
203 | public static function create( $args = array() ) {
204 | global $wpdb;
205 |
206 | $r = self::validate( $args );
207 |
208 | // Bail if missing login or email
209 | if ( empty( $r['user_login'] ) || empty( $r['user_email'] ) ) {
210 | return new WP_Error( 'wp_signups_empty_id', esc_html__( 'Signup not found.', 'wp-user-signups' ), $r );
211 | }
212 |
213 | // Check for previous signup
214 | $query = new WP_Signup_Query();
215 | $existing = $query->query( array(
216 | 'user_email' => $r['user_email'],
217 | 'number' => 1
218 | ) );
219 |
220 | // Domain exists already...
221 | if ( ! empty( $existing ) ) {
222 | return new WP_Error( 'wp_signups_domain_exists', esc_html__( 'That signup already exists.', 'wp-user-signups' ), $existing );
223 | }
224 |
225 | // Create the signup!
226 | $prev_errors = ! empty( $GLOBALS['EZSQL_ERROR'] ) ? $GLOBALS['EZSQL_ERROR'] : array();
227 | $suppress = $wpdb->suppress_errors( true );
228 | $format = array( '%s', '%s', '%s', '%s', '%s', '%s', '%s', '%d', '%s', '%s' );
229 | $result = $wpdb->insert( $wpdb->signups, $r, $format );
230 |
231 | $wpdb->suppress_errors( $suppress );
232 |
233 | // Other error. We suppressed errors before, so we need to make sure
234 | // we handle that now.
235 | if ( empty( $result ) ) {
236 | $recent_errors = array_diff_key( $GLOBALS['EZSQL_ERROR'], $prev_errors );
237 |
238 | while ( count( $recent_errors ) > 0 ) {
239 | $error = array_shift( $recent_errors );
240 | $wpdb->print_error( $error['error_str'] );
241 | }
242 |
243 | return new WP_Error( 'wp_signups_insert_failed', esc_html__( 'Signup creation failed.', 'wp-user-signups' ), $recent_errors );
244 | }
245 |
246 | // Ensure the cache is flushed
247 | clean_signup_cache( $wpdb->insert_id );
248 |
249 | // Prime the cache
250 | $signup = static::get_instance( $wpdb->insert_id );
251 |
252 | /**
253 | * Fires after a signup has been created.
254 | *
255 | * @param WP_Signup $signup The signup object.
256 | */
257 | do_action( 'wp_signups_created', $signup );
258 |
259 | // Sent notifications
260 | $signup->notify();
261 |
262 | return $signup;
263 | }
264 |
265 | /**
266 | * Activate a sign-up
267 | *
268 | * @see wpmu_activate_signup()
269 | *
270 | * @since 1.0.0
271 | *
272 | * @global WPDB $wpdb
273 | * @return WP_Error
274 | */
275 | public function activate() {
276 | global $wpdb;
277 |
278 | // Already active
279 | if ( true === (bool) $this->active ) {
280 | return empty( $this->domain )
281 | ? new WP_Error( 'already_active', esc_html__( 'The user is already active.', 'wp-user-signups' ), $this )
282 | : new WP_Error( 'already_active', esc_html__( 'The site is already active.', 'wp-user-signups' ), $this );
283 | }
284 |
285 | // Prepare some signup info
286 | $meta = maybe_unserialize( $this->meta );
287 | $password = wp_generate_password( 12, false );
288 |
289 | // Check for username & email addresses
290 | $un_id = username_exists( $this->user_login );
291 | $em_id = email_exists( $this->user_email );
292 |
293 | // Try to create user
294 | if ( ( false === $un_id ) && ( false === $em_id ) ) {
295 | $user_id = is_multisite()
296 | ? wpmu_create_user( $this->user_login, $password, $this->user_email )
297 | : wp_create_user( $this->user_login, $password, $this->user_email );
298 |
299 | // Bail if no user was created
300 | if ( empty( $user_id ) ) {
301 | return new WP_Error( 'already_active', esc_html__( 'The user is already active.', 'wp-user-signups' ), $this );
302 | }
303 |
304 | // Username is already registered
305 | } elseif ( false !== $un_id ) {
306 | return new WP_Error( 'already_active', esc_html__( 'This username is already in use.', 'wp-user-signups' ), $this );
307 |
308 | // Email is already registered
309 | } elseif ( false !== $em_id ) {
310 | return new WP_Error( 'already_active', esc_html__( 'This email address is already in use.', 'wp-user-signups' ), $this );
311 | }
312 |
313 | // Get the current time, we'll use it in a few places
314 | $now = current_time( 'mysql', true );
315 |
316 | // Parse args
317 | $args = wp_parse_args( array(
318 | 'active' => 1,
319 | 'activated' => $now
320 | ), (array) $this->data );
321 |
322 | // Update the signup
323 | $updated = $this->update( $args );
324 |
325 | // Bail if update failed
326 | if ( is_wp_error( $updated ) ) {
327 | return new WP_Error( 'activation_failed', esc_html__( 'Sign up activation failed.', 'wp-user-signups' ), $this );
328 | }
329 |
330 | // Default return value
331 | $retval = array(
332 | 'user_id' => $user_id,
333 | 'password' => $password,
334 | 'meta' => $meta
335 | );
336 |
337 | // Try to create a site
338 | if ( empty( $this->domain ) ) {
339 |
340 | /**
341 | * Fires immediately after a new user is activated.
342 | *
343 | * @since MU
344 | *
345 | * @param int $user_id User ID.
346 | * @param int $password User password.
347 | * @param array $meta Signup meta data.
348 | */
349 | do_action( 'wpmu_activate_user', $user_id, $password, $meta );
350 |
351 | // Try to create a site
352 | } elseif ( is_multisite() ) {
353 | $blog_id = wpmu_create_blog( $this->domain, $this->path, $this->title, $user_id, $meta, $wpdb->siteid );
354 |
355 | // Created a user but cannot create a site
356 | if ( is_wp_error( $blog_id ) ) {
357 | $blog_id->add_data( $this );
358 | return $blog_id;
359 | }
360 |
361 | /**
362 | * Fires immediately after a site is activated.
363 | *
364 | * @since MU
365 | *
366 | * @param int $blog_id Blog ID.
367 | * @param int $user_id User ID.
368 | * @param int $password User password.
369 | * @param string $title Site title.
370 | * @param array $meta Signup meta data.
371 | */
372 | do_action( 'wpmu_activate_blog', $blog_id, $user_id, $password, $this->title, $meta );
373 |
374 | // Add site-specific data to return value
375 | $retval['blog_id'] = $blog_id;
376 | $retval['title'] = $this->title;
377 | }
378 |
379 | return $retval;
380 | }
381 |
382 | /**
383 | * Execute actions responsible for triggering notification emails to users
384 | * who have signed up for accounts, maybe with a new site too.
385 | *
386 | * @since 1.0.0
387 | */
388 | public function notify() {
389 |
390 | // Site action
391 | if ( ! empty( $this->domain ) && ! empty( $this->path ) ) {
392 |
393 | /**
394 | * Fires after signup information has been written to the database.
395 | *
396 | * @since 4.4.0
397 | *
398 | * @param string $domain The requested domain.
399 | * @param string $path The requested path.
400 | * @param string $title The requested site title.
401 | * @param string $user The user's requested login name.
402 | * @param string $user_email The user's email address.
403 | * @param string $key The user's activation key
404 | * @param array $meta By default, contains the requested privacy setting and lang_id.
405 | */
406 | do_action( 'after_signup_site', $this->domain, $this->path, $this->title, $this->user_login, $this->user_email, $this->key, $this->meta );
407 |
408 | // User action
409 | } else {
410 |
411 | /**
412 | * Fires after a user's signup information has been written to the database.
413 | *
414 | * @since 4.4.0
415 | *
416 | * @param string $user The user's requested login name.
417 | * @param string $user_email The user's email address.
418 | * @param string $key The user's activation key
419 | * @param array $meta Additional signup meta. By default, this is an empty array.
420 | */
421 | do_action( 'after_signup_user', $this->user_login, $this->user_email, $this->key, $this->meta );
422 | }
423 | }
424 |
425 | /**
426 | * Validate array of data used for editing or creating a sign-up
427 | *
428 | * @since 1.0.0
429 | *
430 | * @param type $params
431 | */
432 | public static function validate( $params = array() ) {
433 |
434 | // Whitelist keys
435 | $r = array_intersect_key( $params, array(
436 | 'domain' => '',
437 | 'path' => '/',
438 | 'title' => '',
439 | 'user_login' => '',
440 | 'user_email' => '',
441 | 'registered' => '',
442 | 'activated' => '',
443 | 'active' => '',
444 | 'activation_key' => '',
445 | 'meta' => ''
446 | ) );
447 |
448 | // Get current date for use in `registered` and `activated` values
449 | $now = date( 'Y-m-d H:i:s' );
450 |
451 | // User login
452 | if ( isset( $r['user_login'] ) ) {
453 | $r['user_login'] = preg_replace( '/\s+/', '', sanitize_user( $r['user_login'], true ) );
454 | }
455 |
456 | // Sanitize email
457 | if ( isset( $r['user_email'] ) ) {
458 | $r['user_email'] = sanitize_email( $r['user_email'] );
459 | }
460 |
461 | // Registered date
462 | if ( ! empty( $r['registered'] ) ) {
463 | $r['registered'] = date( 'Y-m-d H:i:s', strtotime( $r['registered'] ) );
464 | } else {
465 | $r['registered'] = $now;
466 | }
467 |
468 | // Activated date
469 | if ( ! empty( $r['activated'] ) && ( '0000-00-00 00:00:00' !== $r['activated'] ) ) {
470 | $r['activated'] = date( 'Y-m-d H:i:s', strtotime( $r['activated'] ) );
471 | } else {
472 | $r['activated'] = '0000-00-00 00:00:00';
473 | }
474 |
475 | // Activated
476 | if ( isset( $r['active'] ) ) {
477 | $r['active'] = (int) $r['active'];
478 |
479 | // Set activated to now if activating for the first time
480 | if ( ! empty( $r['active'] ) && ( '0000-00-00 00:00:00' === $r['activated'] ) ) {
481 | $r['activated'] = $now;
482 | }
483 | }
484 |
485 | // Activation key
486 | if ( empty( $r['activation_key'] ) ) {
487 |
488 | $base = false;
489 |
490 | // Site & User keys are based on different things
491 | if ( ! empty( $r['domain'] ) && ! empty( $r['path'] ) ) {
492 | $base = $r['domain'];
493 | } elseif ( ! empty( $r['user_email'] ) ) {
494 | $base = $r['user_email'];
495 | }
496 |
497 | // Maybe set key if base is good
498 | if ( ! empty( $base ) ) {
499 | $r['activation_key'] = substr( md5( time() . rand() . $base ), 0, 16 );
500 | }
501 | }
502 |
503 | // Meta array (this is wack)
504 | if ( isset( $r['meta'] ) ) {
505 |
506 | // Sanitize meta
507 | if ( is_array( $r['meta'] ) ) {
508 | array_walk( $r['meta'], 'sanitize_text_field' );
509 | } else {
510 | $r['meta'] = sanitize_text_field( $r['meta'] );
511 | }
512 |
513 | // Serialize for saving
514 | $r['meta'] = maybe_serialize( $r['meta'] );
515 | }
516 |
517 | return $r;
518 | }
519 | }
520 |
--------------------------------------------------------------------------------
/wp-user-signups/includes/classes/class-wp-signups-list-table.php:
--------------------------------------------------------------------------------
1 | active = (int) ! empty( $_GET['active'] );
42 | $this->statuses = wp_signups_get_statuses();
43 |
44 | parent::__construct();
45 | }
46 |
47 | /**
48 | * Prepare items for the list table
49 | *
50 | * @since 1.0.0
51 | */
52 | public function prepare_items() {
53 |
54 | // Orderby
55 | $orderby = isset( $_GET['orderby'] )
56 | ? sanitize_key( $_GET['orderby'] )
57 | : '';
58 |
59 | // Orderby
60 | $order = isset( $_GET['order'] )
61 | ? sanitize_key( $_GET['order'] )
62 | : 'asc';
63 |
64 | // Limit
65 | $per_page = $this->get_items_per_page( 'edit_signups_per_page' );
66 | $page_num = isset( $_GET['paged'] ) ? absint( $_GET['paged'] ) : 0;
67 | $paged = max( 1, $page_num );
68 |
69 | // Total
70 | $type = wp_filter_object_list( $this->statuses, array( 'value' => $this->active ), 'and', 'count' );
71 | $total = reset( $type );
72 |
73 | // Query for signups
74 | $query = new WP_Signup_Query( array(
75 | 'active' => $this->active,
76 | 'orderby' => $orderby,
77 | 'order' => $order,
78 | 'offset' => ( $paged * $per_page ) - $per_page,
79 | 'number' => $per_page
80 | ) );
81 |
82 | // Set items if any are found
83 | if ( ! empty( $query->signups ) && ! is_wp_error( $query->signups ) ) {
84 | $this->items = $query->signups;
85 | }
86 |
87 | // Pagination
88 | $this->set_pagination_args( array(
89 | 'total_items' => $total,
90 | 'per_page' => $per_page
91 | ) );
92 | }
93 |
94 | /**
95 | * Get columns for the table
96 | *
97 | * @since 1.0.0
98 | *
99 | * @return array Map of column ID => title
100 | */
101 | public function get_columns() {
102 |
103 | // All columns
104 | $columns = array(
105 | 'cb' => '',
106 | 'user' => _x( 'User', 'wp-user-signups' ),
107 | 'site' => _x( 'Site', 'wp-user-signups' ),
108 | 'activation_key' => _x( 'Key', 'wp-user-signups' ),
109 | 'registered' => _x( 'Registered', 'wp-user-signups' ),
110 | 'activated' => _x( 'Activated', 'wp-user-signups' )
111 | );
112 |
113 | // Remove site column if single-site
114 | if ( ! wp_signups_is_multisite() ) {
115 | unset( $columns['site'] );
116 | }
117 |
118 | return $columns;
119 | }
120 |
121 | /**
122 | * Return sortable columns
123 | *
124 | * @since 1.0.0
125 | *
126 | * @return array
127 | */
128 | public function get_sortable_columns() {
129 | return array(
130 | 'user' => 'user_login',
131 | 'registered' => 'registered',
132 | 'activated' => 'activated'
133 | );
134 | }
135 |
136 | /**
137 | * Get an associative array ( option_name => option_title ) with the list
138 | * of bulk actions available on this table.
139 | *
140 | * @since 1.0.0
141 | * @access protected
142 | *
143 | * @return array
144 | */
145 | protected function get_bulk_actions() {
146 |
147 | // Default actions
148 | $actions = array(
149 | 'activate' => esc_html__( 'Activate', 'wp-user-signups' ),
150 | 'resend' => esc_html__( 'Resend', 'wp-user-signups' ),
151 | 'delete' => esc_html__( 'Delete', 'wp-user-signups' )
152 | );
153 |
154 | // Remove activate action of already viewing active sign-ups
155 | if ( 1 === $this->active ) {
156 | unset( $actions['activate'] );
157 | }
158 |
159 | return apply_filters( 'wp_signups_bulk_actions', $actions );
160 | }
161 |
162 | /**
163 | * Display the bulk actions dropdown.
164 | *
165 | * @since 1.0.0
166 | * @access protected
167 | *
168 | * @param string $which The location of the bulk actions: 'top' or 'bottom'.
169 | * This is designated as optional for backwards-compatibility.
170 | */
171 | protected function bulk_actions( $which = '' ) {
172 | if ( is_null( $this->_actions ) ) {
173 | $no_new_actions = $this->_actions = $this->get_bulk_actions();
174 | /**
175 | * Filter the list table Bulk Actions drop-down.
176 | *
177 | * The dynamic portion of the hook name, $this->screen->id, refers
178 | * to the ID of the current screen, usually a string.
179 | *
180 | * This filter can currently only be used to remove bulk actions.
181 | *
182 | * @since 3.5.0
183 | *
184 | * @param array $actions An array of the available bulk actions.
185 | */
186 | $this->_actions = apply_filters( "bulk_actions-{$this->screen->id}", $this->_actions );
187 | $this->_actions = array_intersect_assoc( $this->_actions, $no_new_actions );
188 | $two = '';
189 | wp_nonce_field( 'signups-bulk' );
190 | } else {
191 | $two = '2';
192 | }
193 |
194 | if ( empty( $this->_actions ) ) {
195 | return;
196 | }
197 |
198 | echo "";
199 | echo "\n";
211 | submit_button( __( 'Apply', 'wp-user-signups' ), 'action', false, false, array( 'id' => "doaction{$two}" ) );
212 | echo "\n";
213 | }
214 |
215 | /**
216 | * Get the current action selected from the bulk actions dropdown.
217 | *
218 | * @since 1.0.0
219 | *
220 | * @return string|bool The action name or False if no action was selected
221 | */
222 | public function current_action() {
223 |
224 | if ( isset( $_REQUEST['bulk_action'] ) && -1 != $_REQUEST['bulk_action'] ) {
225 | return $_REQUEST['bulk_action'];
226 | }
227 |
228 | if ( isset( $_REQUEST['bulk_action2'] ) && -1 != $_REQUEST['bulk_action2'] ) {
229 | return $_REQUEST['bulk_action2'];
230 | }
231 |
232 | return false;
233 | }
234 |
235 | /**
236 | * Custom no items text
237 | *
238 | * @since 1.0.0
239 | *
240 | * @access public
241 | */
242 | public function no_items() {
243 | if ( 1 === $this->active ) {
244 | esc_html_e( 'No active sign-ups.', 'wp-user-signups' );
245 | } elseif ( 0 === $this->active ) {
246 | esc_html_e( 'No pending sign-ups.', 'wp-user-signups' );
247 | } else {
248 | esc_html_e( 'No sign-ups found.', 'wp-user-signups' );
249 | }
250 | }
251 |
252 | /**
253 | * Get an array of sign-ups views
254 | *
255 | * @since 1.0.0
256 | *
257 | * @return array
258 | */
259 | protected function get_views() {
260 |
261 | // Get statuses
262 | $view_links = array();
263 |
264 | // Loop through statuses
265 | foreach ( $this->statuses as $status ) {
266 |
267 | // Current class
268 | $class = ( $status->value === $this->active )
269 | ? 'current'
270 | : '';
271 |
272 | // Status args
273 | $args = ( 'activated' === $status->id )
274 | ? array( 'active' => (int) $status->value )
275 | : array();
276 |
277 | // Build the URL for the status
278 | $url = wp_signups_admin_url( $args );
279 |
280 | // Add link to array
281 | $view_links[ $status->id ] = "" . sprintf( _nx( $status->name . ' (%s)', $status->name . ' (%s)', $status->count, 'users' ), number_format_i18n( $status->count ) ) . '';
282 | }
283 |
284 | // Return links
285 | return $view_links;
286 | }
287 |
288 | /**
289 | * Get cell value for the checkbox column
290 | *
291 | * @since 1.0.0
292 | * @access protected
293 | *
294 | * @param WP_Signup $signup Current sign-up item
295 | * @return string HTML for the cell
296 | */
297 | protected function column_cb( $signup ) {
298 | $signup_id = $signup->signup_id;
299 | $domain = $signup->domain;
300 |
301 | return ''
303 | . '';
305 | }
306 |
307 | /**
308 | * Get cell value for the domain column
309 | *
310 | * @since 1.0.0
311 | * @access protected
312 | *
313 | * @param WP_Signup $signup Current sign-up item
314 | * @return string HTML for the cell
315 | */
316 | protected function column_user( $signup ) {
317 |
318 | // Default empty actions
319 | $actions = array();
320 |
321 | // Get vars
322 | $login = $signup->user_login;
323 | $email = $signup->user_email;
324 | $signup_id = (int) $signup->signup_id;
325 | $active = (bool) $signup->active;
326 |
327 | // Edit
328 | $edit_link = wp_signups_admin_url( array(
329 | 'signup_ids' => $signup_id,
330 | 'page' => 'signup_edit',
331 | 'referrer' => wp_signups_is_list_page()
332 | ? 'network'
333 | : 'site'
334 | ) );
335 |
336 | // Active
337 | $text = __( 'Activate', 'wp-user-signups' );
338 | $action = 'activate';
339 |
340 | // Default args
341 | $args = array(
342 | 'action' => $action,
343 | 'signup_ids' => $signup_id,
344 | '_wpnonce' => wp_create_nonce( 'signups-bulk' )
345 | );
346 |
347 | $status_link = wp_signups_admin_url( $args );
348 |
349 | // Resend
350 | $resend_args = $args;
351 | $resend_args['action'] = 'resend';
352 | $resend_link = wp_signups_admin_url( $resend_args );
353 |
354 | // Delete
355 | $delete_args = $args;
356 | $delete_args['action'] = 'delete';
357 | $delete_link = wp_signups_admin_url( $delete_args );
358 |
359 | // Edit
360 | if ( current_user_can( 'edit_signup', $signup_id ) ) {
361 | $actions['edit'] = sprintf( '%s', esc_url( $edit_link ), esc_html__( 'Edit', 'wp-user-signups' ) );
362 | }
363 |
364 | // Resend
365 | if ( current_user_can( 'resend_signup', $signup_id ) ) {
366 | $actions['resend'] = sprintf( '%s', esc_url( $resend_link ), esc_html__( 'Resend', 'wp-user-signups' ) );
367 | }
368 |
369 | // Activate
370 | if ( ( false === $active ) && current_user_can( "{$action}_signup", $signup_id ) ) {
371 | $actions[ $action ] = sprintf( '%s', esc_url( $status_link ), esc_html( $text ) );
372 | }
373 |
374 | // Delete
375 | if ( current_user_can( 'delete_signup', $signup_id ) ) {
376 | $actions['delete'] = sprintf( '%s', esc_url( $delete_link ), esc_html__( 'Delete', 'wp-user-signups' ) );
377 | }
378 |
379 | // Get HTML from actions
380 | $action_html = $this->row_actions( $actions, false );
381 |
382 | return '' . esc_html( $login ) . ' — ' . esc_html( $email ) . $action_html;
383 | }
384 |
385 | /**
386 | * Get value for the email column
387 | *
388 | * @since 1.0.0
389 | * @access protected
390 | *
391 | * @param WP_Signup $signup Current sign-up item
392 | * @return string HTML for the cell
393 | */
394 | protected function column_user_email( $signup ) {
395 | return $signup->user_email;
396 | }
397 |
398 | /**
399 | * Get value for the site column, made of domain & path
400 | *
401 | * @since 1.0.0
402 | * @access protected
403 | *
404 | * @param WP_Signup $signup Current sign-up item
405 | * @return string HTML for the cell
406 | */
407 | protected function column_site( $signup ) {
408 | return ! empty( $signup->domain ) && ! empty( $signup->path )
409 | ? $signup->domain . $signup->path
410 | : '—';
411 | }
412 |
413 | /**
414 | * Get value for the key column
415 | *
416 | * @since 1.0.0
417 | * @access protected
418 | *
419 | * @param WP_Signup $signup Current sign-up item
420 | * @return string HTML for the cell
421 | */
422 | protected function column_activation_key( $signup ) {
423 | return '' . $signup->activation_key . '';
424 | }
425 |
426 | /**
427 | * Get value for the status column
428 | *
429 | * @since 1.0.0
430 | * @access protected
431 | *
432 | * @param WP_Signup $signup Current sign-up item
433 | *
434 | * @return string HTML for the cell
435 | */
436 | protected function column_registered( $signup ) {
437 | return mysql2date( get_option( 'date_format' ), $signup->registered ) . ' ' .
438 | mysql2date( get_option( 'time_format' ), $signup->registered );
439 | }
440 |
441 | /**
442 | * Get value for the status column
443 | *
444 | * @since 1.0.0
445 | * @access protected
446 | *
447 | * @param WP_Signup $signup Current sign-up item
448 | *
449 | * @return string HTML for the cell
450 | */
451 | protected function column_activated( $signup ) {
452 |
453 | // Not yet active
454 | if ( '0000-00-00 00:00:00' === $signup->activated ) {
455 | return esc_html__( '—', 'wp-user-signups' );
456 |
457 | // Activated
458 | } else {
459 | return mysql2date( get_option( 'date_format' ), $signup->activated ) . ' ' .
460 | mysql2date( get_option( 'time_format' ), $signup->activated );
461 | }
462 | }
463 | }
464 |
--------------------------------------------------------------------------------
/wp-user-signups/includes/functions/admin.php:
--------------------------------------------------------------------------------
1 | 20,
77 | 'option' => 'edit_signups_per_page',
78 | 'label' => _x( 'Sign ups', 'Signups per page (screen options)', 'wp-user-signups' )
79 | )
80 | );
81 | }
82 |
83 | /**
84 | * Help save the per_page screen option
85 | *
86 | * @since 1.0.0
87 | *
88 | * @param string $status
89 | * @param string $option
90 | * @param int $value
91 | *
92 | * @return string
93 | */
94 | function wp_signups_set_screen_option( $status = '', $option = '', $value = 20 ) {
95 |
96 | if ( 'edit_signups_per_page' === $option ) {
97 | return $value;
98 | }
99 |
100 | return $status;
101 | }
102 |
103 | /**
104 | * Load the list table and populate some essentials
105 | *
106 | * @since 1.0.0
107 | */
108 | function wp_signups_load_list_table() {
109 | global $wp_list_table;
110 |
111 | // Include the list table class
112 | require_once wp_signups_get_plugin_path() . 'includes/classes/class-wp-signups-list-table.php';
113 |
114 | // Create a new list table object
115 | $wp_list_table = new WP_Signups_List_Table();
116 |
117 | $wp_list_table->prepare_items();
118 | }
119 |
120 | /**
121 | * Output the admin page header
122 | *
123 | * @since 1.0.0
124 | *
125 | * @param int $signup_id Signup ID
126 | */
127 | function wp_signups_output_page_header( $signup_id = 0 ) {
128 | global $title;
129 |
130 | // List
131 | if ( wp_signups_is_list_page() ) {
132 |
133 | // With "Add new" link
134 | if ( current_user_can( 'create_signups' ) ) {
135 | $link_url = wp_signups_admin_url( array(
136 | 'page' => 'signup_edit'
137 | ) );
138 | $title_link = '' . esc_html__( 'Add New', 'wp-user-signups' ) . '';
139 | $title = sprintf( esc_html__( 'Sign ups %s', 'wp-user-signups' ), $title_link );
140 |
141 | // Without "Add new" link
142 | } else {
143 | $title = esc_html__( 'Sign ups', 'wp-user-signups' );
144 | }
145 |
146 | // Add/Edit
147 | } else {
148 |
149 | // Add
150 | if ( empty( $signup_id ) || ! empty( $_POST['_wpnonce'] ) ) {
151 | $title = esc_html__( 'Add New Sign up', 'wp-user-signups' );
152 |
153 | // Edit
154 | } else {
155 | $title = esc_html__( 'Edit Sign up', 'wp-user-signups' );
156 | }
157 | }
158 |
159 | // This is copied from WordPress core
160 | ?>