GDPR - Version 1.4.7

Version Description

  • Fix for users who were complaining about their scroll bars missing if they did not select a privacy policy page.
Download this release

Release Info

Developer fclaussen
Plugin Icon 128x128 GDPR
Version 1.4.7
Comparing to
See all releases

Version 1.4.7

Files changed (54) hide show
  1. LICENSE.txt +339 -0
  2. README.txt +377 -0
  3. admin/class-gdpr-admin.php +894 -0
  4. admin/class-gdpr-requests-admin.php +359 -0
  5. admin/class-gdpr-telemetry.php +517 -0
  6. admin/index.php +1 -0
  7. admin/partials/index.php +1 -0
  8. admin/partials/requests.php +323 -0
  9. admin/partials/settings.php +324 -0
  10. admin/partials/templates/index.php +1 -0
  11. admin/partials/templates/tmpl-consents.php +28 -0
  12. admin/partials/templates/tmpl-cookies.php +71 -0
  13. admin/partials/templates/tmpl-tools.php +49 -0
  14. admin/partials/tools.php +135 -0
  15. assets/css/gdpr-admin.css +1 -0
  16. assets/css/gdpr-public.css +1 -0
  17. assets/js/gdpr-admin.js +1 -0
  18. assets/js/gdpr-public.js +1 -0
  19. gdpr.php +78 -0
  20. includes/class-gdpr-activator.php +44 -0
  21. includes/class-gdpr-audit-log.php +170 -0
  22. includes/class-gdpr-deactivator.php +38 -0
  23. includes/class-gdpr-email.php +229 -0
  24. includes/class-gdpr-help.php +188 -0
  25. includes/class-gdpr-requests.php +265 -0
  26. includes/class-gdpr.php +659 -0
  27. includes/helper-functions.php +120 -0
  28. includes/index.php +1 -0
  29. index.php +1 -0
  30. languages/gdpr.pot +1243 -0
  31. public/class-gdpr-public.php +359 -0
  32. public/class-gdpr-requests-public.php +392 -0
  33. public/index.php +1 -0
  34. public/partials/complaint-form.php +13 -0
  35. public/partials/confirmation-screens.php +80 -0
  36. public/partials/delete-form.php +24 -0
  37. public/partials/export-data-form.php +11 -0
  38. public/partials/index.php +1 -0
  39. public/partials/privacy-bar.php +27 -0
  40. public/partials/privacy-preferences-modal.php +142 -0
  41. public/partials/reconsent-modal.php +34 -0
  42. public/partials/rectify-form.php +12 -0
  43. templates/email/complaint-request.php +26 -0
  44. templates/email/complaint-resolved.php +5 -0
  45. templates/email/data-breach-notification.php +34 -0
  46. templates/email/data-breach-request.php +41 -0
  47. templates/email/delete-request.php +21 -0
  48. templates/email/delete-resolved.php +14 -0
  49. templates/email/export-data-request.php +19 -0
  50. templates/email/index.php +1 -0
  51. templates/email/new-request.php +11 -0
  52. templates/email/rectify-request.php +26 -0
  53. templates/email/rectify-resolved.php +5 -0
  54. uninstall.php +31 -0
LICENSE.txt ADDED
@@ -0,0 +1,339 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ <one line to give the program's name and a brief idea of what it does.>
294
+ Copyright (C) <year> <name of author>
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.
README.txt ADDED
@@ -0,0 +1,377 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === GDPR ===
2
+ Contributors: fclaussen, matthewfarlymn, trewknowledge
3
+ Donate link: http://gdpr-wp.com/donate/
4
+ Tags: gdpr, compliance, privacy, law, general data protection regulation
5
+ Requires at least: 4.7
6
+ Requires PHP: 5.6
7
+ Tested up to: 4.9
8
+ Stable tag: 1.4.7
9
+ License: GPLv2 or later
10
+ License URI: http://www.gnu.org/licenses/gpl-2.0.html
11
+
12
+ This plugin is meant to assist with the GDPR obligations of a Data processor and Controller.
13
+
14
+ == Description ==
15
+
16
+ This plugin is meant to assist a Controller, Data Processor, and Data Protection Officer (DPO) with efforts to meet the obligations and rights enacted under the GDPR.
17
+
18
+ == Documentation ==
19
+ [http://gdpr-wp.com/knowledge-base/](http://gdpr-wp.com/knowledge-base/)
20
+
21
+ == Collaboration ==
22
+
23
+ You can send your pull request at [https://github.com/trewknowledge/gdpr](https://github.com/trewknowledge/gdpr)
24
+
25
+ == Shortcodes & helper functions ==
26
+ [http://gdpr-wp.com/knowledge-base/functions-shortcodes/](http://gdpr-wp.com/knowledge-base/functions-shortcodes/)
27
+
28
+ == Features ==
29
+
30
+ * Consent management
31
+ * Privacy Preference management for Cookies with front-end preference UI & banner notifications
32
+ * Privacy Policy page configurations with version control and re-consent management
33
+ * Rights to erasure & deletion of website data with a double opt-in confirmation email
34
+ * Re-assignment of user data on erasure requests & pseudonymization of user website data
35
+ * Data Processor settings and publishing of contact information
36
+ * Right to access data by admin dashboard with email look up and export
37
+ * Right to access data by Data Subject with front-end requests button & double opt-in confirmation email
38
+ * Right to portability & export of data by Admin or Data Subject in XML or JSON formats
39
+ * Encrypted audit logs for the lifetime of Data Subject compliance activity
40
+ * Data Subject Secret Token for two-factor decryption and recovery of data
41
+ * Data breach notification logs and batch email notifications to Data Subjects
42
+ * Telemetry Tracker for visualizing plugins and website data
43
+
44
+ == Settings ==
45
+
46
+ **General**
47
+
48
+ From the Settings options in the dashboard, you can select the Privacy Policy page for tracking and logging consent.
49
+
50
+ On login, the user must consent to the Privacy Policy outlined on the site. If the user does not consent, the user will not be registered or logged in.
51
+
52
+ If the site owner updates the Privacy Policy page content, the change will be logged and flagged to the admin that they must notify users on next login to seek re-consent. Additionally, the warning message can be dismissed in the event of a minor correction or mistake.
53
+
54
+ Additionally, under General Settings the Admin can set the outgoing email limitation which would set the batch notification email limit per hour in the event of a Breach Notification.
55
+
56
+ **Cookie Preference Management**
57
+
58
+ Similar to consent management, users can opt in or out of cookies that are being used on the site. There are 3 formats of cookies that can be created which include:
59
+
60
+ * **Always Active:** Cookies that are always active or are required for the site to function.
61
+ * **Toggled:** Cookies that can be activated or blocked based on the user preference
62
+ * **Opt-Out Link:** Cookies that require configuration from a third-party source in order to opt-out
63
+
64
+ Depending on the user preference setting, you can use the `is_allowed_cookie( $cookie )` function to save and set the cookies. The cookie with the user approved cookies can be found at another cookie named `gdpr_approved_cookies`. There's also a helper function called `is_allowed_cookie( $cookie )` that you can use to prevent setting up a cookie.
65
+
66
+ **Consent Management**
67
+
68
+ Consents can be registered on the settings page. They can be optional or not. By default, this plugin comes with a Privacy Policy consent that users need to agree with on registration.
69
+
70
+ For optional consents, there's a wrapper function `have_consent( $consent_id )` to help you display or hide something on the site depending if the user gave consent or not.
71
+
72
+ Consents are logged to the user record for auditing or for access purposes.
73
+
74
+
75
+ == Requests Table & Rights of Data Subject ==
76
+
77
+ **Right to Erasure Requests**
78
+
79
+ 1. The Data Subject is able to submit a request to be erased from the site using a shortcode.
80
+ 1. When a request is made, the Data Subject will receive an email confirmation to confirm the deletion request.
81
+
82
+ 1. After email confirmation, the user request is added to the requests table for review by the Administrator. The Administrator can also add a user manually with an email look up and review.
83
+ 1. If the Data Subject has content published on the site for any post types or comments, they will be added to this table. If they do not have any content, they will receive a confirmation of erasure request and be provided a 6 digit Token for safekeeping after erasure in case of recover data needs.
84
+ 1. The requests table allows the Administrator to reassign any content to another user or delete it.
85
+ 1. In the event of comments, the Data Subject’s content would be made anonymous.
86
+
87
+ 1. Admin can also manually add users to the erasure requests table with a manual email search
88
+
89
+ **Right to Access Data Request & User Data Portability**
90
+
91
+ 1. The Data Subject can place a request to download their data with the shortcode.
92
+ 1. After requesting their data, the user will receive a double opt-in confirmation email then the plugin will generate an XML or JSON file, which will be emailed to them for download with an expiration time of 48 hours.
93
+
94
+ **Right to Rectify & Complaint Requests**
95
+
96
+ 1. The Data Subject can place a request to rectify data or file a complaint with the shortcode.
97
+ 1. After making their request, the user will receive a double opt-in confirmation email and then add them to the table for admin to handle the request.
98
+
99
+
100
+ == Tools ==
101
+
102
+ **Access Data**
103
+
104
+ The Access Data tool allows the Admin to look up a user email and view the data of a particular user. The Admin can download and export the data in a JSON or XML format and provide to the Data Subject if manually requested.
105
+
106
+ NOTE: This method should not be used without the Data Subject confirming their identity.
107
+
108
+ **Audit Log**
109
+
110
+ Everything the Data Subject does from registration, providing consent to the privacy policy, terms of service and other requests are logged and encrypted in a database. Data breach notifications are also logged to all Data Subjects upon confirmation by Controller.
111
+
112
+ 1. Using the Data Subject's email, you can look up and retrieve the user information and display it.
113
+ 1. If the Data Subject has been removed from the site, this encrypted log is deleted from the database and saved as an encrypted file inside the plugin folder.
114
+
115
+ If in the future, the Data Subject makes a complaint or there is a need to recover the data, the user can provide their email address and the 6 digit token they received from the deletion confirmation email to decrypt and retrieve the file.
116
+
117
+ **Data Breach & Notifications**
118
+
119
+ In case of a data breach, the Admin can generate a Data Breach Notification to users by logging the information and confirm the breach through a double opt-in confirmation email. The following information would be recorded in the audit log:
120
+
121
+ 1. Nature of the personal data breach
122
+ 1. Name and contact details of the data protection officer
123
+ 1. Likely consequences of the personal data breach
124
+ 1. Measures were taken or proposed to be taken
125
+
126
+ Once the confirmation of the breach has been confirmed via email, the website will begin a batch email notification process to all users every hour until all users receive the notification.
127
+
128
+ == Telemetry Tracker ==
129
+
130
+ The Telemetry Tracker feature will display all data that is being sent outside of your server to another destination. It will indicate the plugin or theme responsible, file and line where the data is being sent.
131
+
132
+ WordPress Core and some plugins gather data from your install and send this data to an outside server.
133
+
134
+ WordPress Plugin Repository does not allow plugins to do that, but premium plugins are able to do this because they are not bound by the Plugin repository rules. If you did not explicitly opt-in for this feature you should make a complaint.
135
+
136
+
137
+ == Installation ==
138
+
139
+ 1. Upload the plugin to the `/wp-content/plugins/` directory
140
+ 1. Activate the plugin through the 'Plugins' menu in WordPress
141
+ 1. Fill out all sections of the settings page.
142
+
143
+
144
+ == Important! ==
145
+
146
+ Activating this plugin does not guarantee that an organization is successfully meeting its responsibilities and obligations of GDPR. Individual organizations should assess their unique responsibilities and ensure extra measures are taken to meet any obligations required by law and based on a data protection impact assessment (DPIA).
147
+
148
+
149
+ == Frequently Asked Questions ==
150
+
151
+ = What is GDPR? =
152
+
153
+ This Regulation lays down rules relating to the protection of natural persons with regard to the processing of personal data and rules relating to the free movement of personal data.
154
+
155
+ This Regulation protects fundamental rights and freedoms of natural persons and in particular their right to the protection of personal data.
156
+
157
+ The free movement of personal data within the Union shall be neither restricted nor prohibited for reasons connected with the protection of natural persons with regard to the processing of personal data.
158
+
159
+ = How do Businesses benefit from GDPR? =
160
+
161
+ * Build stronger customer relationships and trust
162
+ * Improve the brand image of the organization and its brand reputation
163
+ * Improve the governance and responsibility of data
164
+ * Enhance the security and commitment to the privacy of the brand
165
+ * Create value-added competitive advantages
166
+
167
+ = When is the GDPR coming into effect? =
168
+
169
+ It will be enforced on May 25th, 2018.
170
+
171
+ = Who does the GDPR affect? =
172
+
173
+ The GDPR applies to all EU organisations – whether commercial business, charity or public authority – that collect, store or process EU residents’ personal data, even if they’re not EU citizens.
174
+
175
+ The GDPR applies to all organisations located within the EU, whether you are a commercial business, charity or public authority, institution and collect, store or process EU citizen data. It also applies to any organisation located outside of the EU if they also collect store or process EU citizen data.
176
+
177
+ = What is considered personal data? =
178
+
179
+ The GDPR defines personal data as any information or type of data that can directly or indirectly identify a natural person’s identity. This can include information such as Name, Address, Email, Photos, System Data, IP addresses, Location data, Phone numbers, and Cookies.
180
+
181
+ For other special categories of personal data, there are more strict regulations for categories such as Race, Religion, Political Views, Sexual Orientation, Health Information, Biometric and Genetic data.
182
+
183
+ = What are the penalties for non-compliance? =
184
+
185
+ Organizations can be fined up to 4% of annual global turnover for breaching GDPR or €20 Million. This is the maximum fine that can be imposed for the most serious infringements.
186
+
187
+ There is a tiered approach to the fines whereby a company can be fined 2% for not having their records in order (Article 28), not notifying the supervising authority and Data Subject about a security breach or for investigating and assessing the breach.
188
+
189
+ = Am I compliant just by activating this plugin? =
190
+
191
+ No, this plugin is meant to assist a Controller, Data Processor, and Data Protection Officer (DPO) with efforts to meet the obligations and rights enacted under the GDPR.
192
+
193
+ Activating this plugin does not guarantee that an organisation is successfully meeting its responsibilities and obligations of GDPR. Organisations should assess their unique responsibilities and ensure extra measures are taken to meet any obligations required by law and based on a data protection impact assessment (DPIA).
194
+
195
+ == Screenshots ==
196
+
197
+ 1. Cookie settings page.
198
+ 2. Cookie notification bar.
199
+ 3. Cookie management modal.
200
+ 4. Registration with consent checkboxes.
201
+ 5. Consent management modal.
202
+ 6. Privacy Policy page updated. Asking for re-consent.
203
+ 7. User deletion review table.
204
+ 8. Telemetry Tracker.
205
+ 9. Audit Log sample.
206
+
207
+ == Changelog ==
208
+
209
+ = 1.4.7 =
210
+ * Fix for users who were complaining about their scroll bars missing if they did not select a privacy policy page.
211
+
212
+ = 1.4.6 =
213
+ * Change re-consent logic so it doesn't influence SEO with repeated content.
214
+
215
+ = 1.4.5 =
216
+ * Minor style adjustments
217
+ * Body scroll is disabled when modal is active
218
+ * Adjusting privacy bar to sit behind re-consent modal
219
+
220
+ = 1.4.4 =
221
+ * Fix all_cookies field being displayed as text field instead of hidden.
222
+
223
+ = 1.4.3 =
224
+ * Found one more instance of Telemetry Scanner, changed to Telemetry Tracker.
225
+ * Delete cookies when users change their preferences and disable some cookies.
226
+ * Changed cookies used field to textarea for easier reading when lots of cookies are set.
227
+ * Added a text to the settings page explaining that even if cookies are registered, if the user does not input some text for the privacy banner, it won't show up.
228
+ * Adding filters for the admin notification email. [https://gdpr-wp.com/knowledge-base/actions-filters/](https://gdpr-wp.com/knowledge-base/actions-filters/)
229
+ * Adding filters for the request forms button label. [https://gdpr-wp.com/knowledge-base/actions-filters/](https://gdpr-wp.com/knowledge-base/actions-filters/)
230
+
231
+ = 1.4.2 =
232
+ * Fix privacy bar reapearing. Cookie was not set to expire in a year.
233
+
234
+ = 1.4.1 =
235
+ * Allow links in the consent description in the wp profile page.
236
+ * Force tabs to be an array when empty to fix the notices and fatal error in the front end.
237
+ * Hide cookies sidebar in the privacy centre window if no cookies were registered.
238
+ * Adding a filter so the privacy bar button text can be changed.
239
+ * Changing Telemetry Scanner to Telemetry Tracker for consistency across the plugin.
240
+ * Translating missing strings.
241
+ * Adding options to add or remove consent checkboxes to woocommerce registration form and checkout registration form.
242
+
243
+ = 1.4.0 =
244
+ * Adding the option to disable the plugin CSS. Be careful when using this option. Make sure you know what you are doing.
245
+ * Adding the option to enable or disable the telemetry feature.
246
+ * Adding the option to add reCaptcha to the request forms.
247
+ * Adding comments to the personal data export.
248
+ * Moved privacy bar content field and privacy excerpt field to the general settings tab.
249
+ * Removed automatic privacy policy link from the privacy bar.
250
+ * We now accept links in the privacy bar content to get around the last change.
251
+ * Changed Telemetry cleanup schedule to hourly.
252
+ * Forcing the privacy bar to stay on the left to avoid CSS incompatibilities.
253
+ * Renaming the tab classes in the admin panel to again avoid incompatibilities.
254
+ * Fix privacy preference centre only showing up when cookies were registered.
255
+
256
+ = 1.3.5 =
257
+ * Fix undefined variable warning.
258
+ * Fix WooCommerce and possibly other plugins nonce manipulation for logged out users. For real this time.
259
+ * Fix XML export fatal error when meta key starts with a number.
260
+
261
+ = 1.3.4 =
262
+ * Prefixed all nonce actions.
263
+ * Fixed cookies being checked by default when they should have been unchecked.
264
+ * Possible fix for strange characters causing XML export to throw an error.
265
+ * Fix for WooCommerce nonce manipulation for logged out users that was preventing visitors from updating their privacy preferences.
266
+
267
+ = 1.3.3 =
268
+ * Fix translation error everybody has been complaining about.
269
+
270
+ = 1.3.2 =
271
+ * Fix issue with the is_allowed_cookie JS function.
272
+
273
+ = 1.3.1 =
274
+ * Fix consent syncing when difference comes from database and not the cookie.
275
+ * Might allow people to use external services like iubenda.
276
+
277
+ = 1.3.0 =
278
+ * Added BuddyPress registration form integration.
279
+ * Added WooCommerce registration and checkout registration form integration.
280
+ * Added admin notifications when a user makes a request that requires interaction.
281
+
282
+ = 1.2.2 =
283
+ * Adding a couple missing translation strings.
284
+ * Wrapping the telemetry post type page in an `if` so people can unregister it if they want to.
285
+
286
+ = 1.2.1 =
287
+ * After one user reported that their scroll bar disappeared I decided to remove the code that do that when the reconsent modal shows up. This has no impact on anything, but it might fix this user problem.
288
+
289
+ = 1.2.0 =
290
+ * Fix has_consent and is_allowed_cookie JavaScript functions not being available globally.
291
+ * Add a function to get the consent checkbox without echoing them.
292
+ * Change how the user deletion request works. We removed the email attachment to avoid being considered spam. The user can now download it immediatelly by clicking on their email link.
293
+ * Adding an option for user deletions always be added to the request review table. That will allow you to remove your users from third-party services before removing them from your site.
294
+
295
+ = 1.1.6 =
296
+ * Fix weird javascript issue that was preventing users from using the "Close my account" feature.
297
+
298
+ = 1.1.5 =
299
+ * The gdpr_request_form PHP function was returning instead of echoing. That is now fixed.
300
+ * Fix issue when syncing consent cookie and database values.
301
+ * Fix issue that prevented the privacy bar from disappearing after saving privacy preferences.
302
+
303
+ = 1.1.4 =
304
+ * Possible fix for cached sites.
305
+ * Added has_consent and is_allowed_cookie functions to javascript.
306
+ * Changed how the privacy bar and re-consent modal show up based on javascript.
307
+ * Better sync of consent and cookies with a cookie.
308
+
309
+ = 1.1.3 =
310
+ * Changed Complaint and Rectification form submit button wording.
311
+ * Added a loading indicator on the reconsent window. Slow servers will not give the impression that this featured is not working anymore.
312
+ * Fixed user notification not showing after confirming deletion email.
313
+ * Fixed consent "required" toggle not displaying the correct state.
314
+ * Added a second confirmation after disagreeing to reconsent.
315
+
316
+
317
+ = 1.1.2 =
318
+ * Fixed reconsent modal not closing after agreeing to the new policy.
319
+
320
+ = 1.1.1 =
321
+ * Forgot to unload jQuery-UI.
322
+
323
+ = 1.1.0 =
324
+ * Merge the two preferences windows into one.
325
+ * [gdpr_preferences] shortcode doesn't need the 'type' attribute to work anymore.
326
+ * Removed jQuery UI from the front end and replaced with our own notification window to keep a consistent color scheme, avoid unnecessary requests and avoid style issues from theme to theme.
327
+ * Allow logged out users to keep track of consents too. ( Those are not logged to the audit log for obvious reasons. )
328
+ * Added a refresh after preferences change so users can display forms or count the user visit and so on depending on the new user consent.
329
+
330
+ = 1.0.6 =
331
+ * Allowing users to add target on their privacy policy links on the consent description.
332
+
333
+ = 1.0.5 =
334
+ * Allow users to use links on their consent descriptions so they can link to their privacy policy or other pages.
335
+
336
+ = 1.0.4 =
337
+ * Added a link to the privacy policy page on the cookie bar and on the cookie preferences window.
338
+ * Added a new option for a text just before the privacy policy link on the cookie bar.
339
+ * Checking if the user actually registered cookies before showing the cookie bar.
340
+
341
+ = 1.0.3 =
342
+ * Added a shortcode for re-opening the cookie or consent management windows.
343
+
344
+ = 1.0.2 =
345
+ * Added new filters for access data so extensions can add more information.
346
+ * Rebuilt the translation pot file and added translation comments.
347
+
348
+ = 1.0.1 =
349
+ * Fix issue on cookie preferences not saving and displaying php errors.
350
+
351
+ = 1.0.0 =
352
+ * Added cookie management screen
353
+ * Added consent management screen
354
+ * Added Telemetry tracker
355
+ * Complete code rewrite
356
+ * Added more types of request
357
+ * Added Help documentation
358
+ * Added new shortcodes
359
+ * Changed to Settings API
360
+
361
+ = 0.1.1 =
362
+ * Set the admin email as the default processor information on activation
363
+ * Settings updated notice is now dismissible
364
+
365
+ = 0.1.0 =
366
+ * Beta version released to the public
367
+
368
+ == Upgrade Notice ==
369
+
370
+ = 1.0.0 =
371
+ This is a major rewrite of the plugin. Things will look different and work differently.
372
+ We tried to keep most things the same so the impact would be minimal.
373
+ This plugin is no longer in BETA.
374
+ Update with care
375
+
376
+ = 0.1.0 =
377
+ This plugin is in beta. Use it at your own discretion.
admin/class-gdpr-admin.php ADDED
@@ -0,0 +1,894 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * The admin-specific functionality of the plugin.
5
+ *
6
+ * @link https://trewknowledge.com
7
+ * @since 1.0.0
8
+ *
9
+ * @package GDPR
10
+ * @subpackage admin
11
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
12
+ */
13
+
14
+ /**
15
+ * The admin-specific functionality of the plugin.
16
+ *
17
+ * Defines the plugin name and version.
18
+ * Enqueue the admin-specific stylesheet and JavaScript.
19
+ *
20
+ * @package GDPR
21
+ * @subpackage admin
22
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
23
+ */
24
+ class GDPR_Admin {
25
+
26
+ /**
27
+ * The ID of this plugin.
28
+ *
29
+ * @since 1.0.0
30
+ * @access private
31
+ * @var string $plugin_name The ID of this plugin.
32
+ */
33
+ private $plugin_name;
34
+
35
+ /**
36
+ * The version of this plugin.
37
+ *
38
+ * @since 1.0.0
39
+ * @access private
40
+ * @var string $version The current version of this plugin.
41
+ */
42
+ private $version;
43
+
44
+ /**
45
+ * Allowed HTML for wp_kses.
46
+ * @since 1.0.5
47
+ * @access private
48
+ * @var array $allowed_html The allowed HTML for wp_kses.
49
+ */
50
+ private $allowed_html;
51
+
52
+ /**
53
+ * Initialize the class and set its properties.
54
+ *
55
+ * @since 1.0.0
56
+ * @param string $plugin_name The name of this plugin.
57
+ * @param string $version The version of this plugin.
58
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
59
+ */
60
+ public function __construct( $plugin_name, $version ) {
61
+ $this->plugin_name = $plugin_name;
62
+ $this->version = $version;
63
+ $this->allowed_html = array(
64
+ 'a' => array(
65
+ 'href' => true,
66
+ 'title' => true,
67
+ 'target' => true,
68
+ ),
69
+ );
70
+ }
71
+
72
+ /**
73
+ * Register the stylesheets for the admin area.
74
+ *
75
+ * @since 1.0.0
76
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
77
+ */
78
+ public function enqueue_styles() {
79
+ add_thickbox();
80
+ wp_enqueue_style( $this->plugin_name, plugin_dir_url( dirname( __FILE__ ) ) . 'assets/css/gdpr-admin.css', array(), $this->version, 'all' );
81
+ }
82
+
83
+ /**
84
+ * Register the JavaScript for the admin area.
85
+ *
86
+ * @since 1.0.0
87
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
88
+ */
89
+ public function enqueue_scripts() {
90
+ wp_enqueue_script( $this->plugin_name, plugin_dir_url( dirname( __FILE__ ) ) . 'assets/js/gdpr-admin.js', array( 'jquery', 'wp-util', 'jquery-ui-sortable' ), $this->version, false );
91
+ }
92
+
93
+ /**
94
+ * Adds a menu page for the plugin with all it's sub pages.
95
+ *
96
+ * @since 1.0.0
97
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
98
+ */
99
+ public function add_menu() {
100
+ $page_title = esc_html__( 'GDPR', 'gdpr' );
101
+ $capability = 'manage_options';
102
+ $parent_slug = 'gdpr-requests';
103
+ $function = array( $this, 'requests_page_template' );
104
+ $icon_url = 'dashicons-id';
105
+
106
+ $requests = get_option( 'gdpr_requests', array() );
107
+ $confirmed_requests = array_filter( $requests, function( $item ) {
108
+ return $item['confirmed'] == true;
109
+ } );
110
+
111
+ $menu_title = esc_html__( 'GDPR', 'gdpr' );
112
+ if ( count( $confirmed_requests ) ) {
113
+ $menu_title = sprintf( esc_html( 'GDPR %s' ), '<span class="awaiting-mod">' . count( $confirmed_requests ) . '</span>' );
114
+ }
115
+
116
+ add_menu_page( $page_title, $menu_title, $capability, $parent_slug, $function, $icon_url );
117
+
118
+ $menu_title = esc_html__( 'Requests', 'gdpr' );
119
+ $menu_slug = 'gdpr-requests';
120
+ $function = array( $this, 'requests_page_template' );
121
+
122
+ $requests_hook = add_submenu_page( $parent_slug, $menu_title, $menu_title, $capability, $menu_slug, $function );
123
+
124
+ $menu_title = esc_html__( 'Tools', 'gdpr' );
125
+ $menu_slug = 'gdpr-tools';
126
+ $function = array( $this, 'tools_page_template' );
127
+
128
+ $tools_hook = add_submenu_page( $parent_slug, $menu_title, $menu_title, $capability, $menu_slug, $function );
129
+
130
+ $menu_title = esc_html__( 'Settings', 'gdpr' );
131
+ $menu_slug = 'gdpr-settings';
132
+ $function = array( $this, 'settings_page_template' );
133
+
134
+ $settings_hook = add_submenu_page( $parent_slug, $menu_title, $menu_title, $capability, $menu_slug, $function );
135
+
136
+
137
+ $menu_slug = 'edit.php?post_type=telemetry';
138
+
139
+ $cpt = 'telemetry';
140
+ $cpt_obj = get_post_type_object( $cpt );
141
+
142
+ if ( $cpt_obj ) {
143
+ add_submenu_page( $parent_slug, $cpt_obj->labels->name, $cpt_obj->labels->menu_name, $capability, $menu_slug );
144
+ }
145
+
146
+
147
+ add_action( "load-{$requests_hook}", array( 'GDPR_Help', 'add_requests_help' ) );
148
+ add_action( "load-{$tools_hook}", array( 'GDPR_Help', 'add_tools_help' ) );
149
+ add_action( "load-{$settings_hook}", array( 'GDPR_Help', 'add_settings_help' ) );
150
+ add_action( "load-edit.php", array( 'GDPR_Help', 'add_telemetry_help' ) );
151
+ }
152
+
153
+ /**
154
+ * Sanitizing user input on the cookie tabs.
155
+ * @since 1.0.0
156
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
157
+ * @param array $tabs The cookie tabs.
158
+ * @return array The sanitized options.
159
+ */
160
+ public function sanitize_cookie_tabs( $tabs ) {
161
+
162
+ $output = array();
163
+ if ( ! is_array( $tabs ) ) {
164
+ return array();
165
+ }
166
+
167
+ foreach ( $tabs as $key => $props ) {
168
+ if ( '' === $props['name'] || '' === $props['how_we_use'] ) {
169
+ unset( $tabs[ $key ] );
170
+ continue;
171
+ }
172
+ $output[ $key ] = array(
173
+ 'name' => sanitize_text_field( wp_unslash( $props['name'] ) ),
174
+ 'always_active' => isset( $props['always_active'] ) ? boolval( $props['always_active'] ) : 0,
175
+ 'how_we_use' => wp_kses_post( $props['how_we_use'] ),
176
+ 'cookies_used' => sanitize_text_field( wp_unslash( $props['cookies_used'] ) ),
177
+ );
178
+
179
+ if ( isset( $props['hosts'] ) ) {
180
+ foreach ( $props['hosts'] as $host_key => $host ) {
181
+ if ( empty( $host['name'] ) || empty( $host['cookies_used'] ) || empty( $host['cookies_used'] ) ) {
182
+ unset( $props['hosts'][ $host_key ] );
183
+ continue;
184
+ }
185
+ $output[ $key ]['hosts'][ $host_key ] = array(
186
+ 'name' => sanitize_text_field( wp_unslash( $host['name'] ) ),
187
+ 'cookies_used' => sanitize_text_field( wp_unslash( $host['cookies_used'] ) ),
188
+ 'optout' => esc_url_raw( $host['optout'] ),
189
+ );
190
+ }
191
+ }
192
+ }
193
+ return $output;
194
+ }
195
+
196
+ /**
197
+ * Register settings.
198
+ * @since 1.0.0
199
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
200
+ */
201
+ public function register_settings() {
202
+ $settings = array(
203
+ 'gdpr_privacy_policy_page' => 'intval',
204
+ 'gdpr_cookie_banner_content' => array( $this, 'sanitize_with_links' ),
205
+ 'gdpr_cookie_privacy_excerpt' => 'sanitize_textarea_field',
206
+ 'gdpr_cookie_popup_content' => array( $this, 'sanitize_cookie_tabs' ),
207
+ 'gdpr_email_limit' => 'intval',
208
+ 'gdpr_consent_types' => array( $this, 'sanitize_consents' ),
209
+ 'gdpr_deletion_needs_review' => 'boolval',
210
+ 'gdpr_disable_css' => 'boolval',
211
+ 'gdpr_enable_telemetry_tracker' => 'boolval',
212
+ 'gdpr_use_recaptcha' => 'boolval',
213
+ 'gdpr_recaptcha_site_key' => 'sanitize_text_field',
214
+ 'gdpr_recaptcha_secret_key' => 'sanitize_text_field',
215
+ 'gdpr_add_consent_checkboxes_registration' => 'boolval',
216
+ 'gdpr_add_consent_checkboxes_checkout' => 'boolval',
217
+ );
218
+ foreach ( $settings as $option_name => $sanitize_callback ) {
219
+ register_setting( 'gdpr', $option_name, array( 'sanitize_callback' => $sanitize_callback ) );
220
+ }
221
+ }
222
+
223
+ /**
224
+ * Sanitize content but allow links.
225
+ * @param string $string The string that will be sanitized.
226
+ * @return string Sanitized string.
227
+ * @since 1.4.0
228
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
229
+ */
230
+ public function sanitize_with_links( $string ) {
231
+ return wp_kses( $string, $this->allowed_html );
232
+ }
233
+
234
+ /**
235
+ * Sanitize the consents option when saving.
236
+ * @since 1.0.0
237
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
238
+ * @param array $consents The consents that were registered.
239
+ * @return array The sanitized consents array.
240
+ */
241
+ public function sanitize_consents( $consents ) {
242
+ $output = array();
243
+ if ( ! is_array( $consents ) ) {
244
+ return $consents;
245
+ }
246
+
247
+ foreach ( $consents as $key => $props ) {
248
+ if ( '' === $props['name'] || '' === $props['description'] ) {
249
+ unset( $consents[ $key ] );
250
+ continue;
251
+ }
252
+ $output[ $key ] = array(
253
+ 'name' => sanitize_text_field( wp_unslash( $props['name'] ) ),
254
+ 'required' => isset( $props['required'] ) ? boolval( $props['required'] ) : 0,
255
+ 'description' => wp_kses( wp_unslash( $props['description'] ), $this->allowed_html ),
256
+ 'registration' => wp_kses( wp_unslash( $props['registration'] ), $this->allowed_html ),
257
+ );
258
+ }
259
+ return $output;
260
+ }
261
+
262
+ /**
263
+ * Settings Page Template
264
+ *
265
+ * @since 1.0.0
266
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
267
+ */
268
+ public function settings_page_template() {
269
+ $privacy_policy_page = get_option( 'gdpr_privacy_policy_page', 0 );
270
+ $tabs = array(
271
+ 'general' => esc_html__( 'General', 'gdpr' ),
272
+ 'cookies' => esc_html__( 'Cookies', 'gdpr' ),
273
+ 'consents' => esc_html__( 'Consents', 'gdpr' ),
274
+ );
275
+
276
+ $tabs = apply_filters( 'gdpr_settings_pages', $tabs );
277
+
278
+ include_once plugin_dir_path( __FILE__ ) . 'partials/templates/tmpl-cookies.php';
279
+ include_once plugin_dir_path( __FILE__ ) . 'partials/templates/tmpl-consents.php';
280
+
281
+ include plugin_dir_path( __FILE__ ) . 'partials/settings.php';
282
+ }
283
+
284
+ /**
285
+ * Requests Page Template.
286
+ *
287
+ * @since 1.0.0
288
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
289
+ */
290
+ public function requests_page_template() {
291
+ $requests = ( array ) get_option( 'gdpr_requests', array() );
292
+
293
+ if ( ! empty( $requests ) ) {
294
+ foreach ( $requests as $index => $request ) {
295
+ if ( ! $request['confirmed'] ) {
296
+ continue;
297
+ }
298
+ ${$request['type']}[ $index ] = $request;
299
+ }
300
+ }
301
+
302
+ $tabs = array(
303
+ 'rectify' => array(
304
+ 'name' => __( 'Rectify Data', 'gdpr' ),
305
+ 'count' => isset( $rectify ) ? count( $rectify ) : 0,
306
+ ),
307
+ 'complaint' => array(
308
+ 'name' => __( 'Complaint', 'gdpr' ),
309
+ 'count' => isset( $complaint ) ? count( $complaint ) : 0,
310
+ ),
311
+ 'delete' => array(
312
+ 'name' => __( 'Erasure', 'gdpr' ),
313
+ 'count' => isset( $delete ) ? count( $delete ) : 0,
314
+ ),
315
+ );
316
+
317
+ include plugin_dir_path( __FILE__ ) . 'partials/requests.php';
318
+ }
319
+
320
+ /**
321
+ * Tools Page Template.
322
+ *
323
+ * @since 1.0.0
324
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
325
+ */
326
+ public function tools_page_template() {
327
+
328
+ $tabs = array(
329
+ 'access' => esc_html__( 'Access Data', 'gdpr' ),
330
+ 'data-breach' => esc_html__( 'Data Breach', 'gdpr' ),
331
+ 'audit-log' => esc_html__( 'Audit Log', 'gdpr' ),
332
+ );
333
+
334
+ include plugin_dir_path( __FILE__ ) . 'partials/tools.php';
335
+ }
336
+
337
+ /**
338
+ * The data markup on the access data page.
339
+ * @since 1.0.0
340
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
341
+ */
342
+ public function access_data() {
343
+ if ( ! isset( $_POST['nonce'], $_POST['email'] ) || ! wp_verify_nonce( sanitize_key( wp_unslash( $_POST['nonce'] ) ), 'gdpr-access-data' ) ) {
344
+ wp_send_json_error();
345
+ }
346
+
347
+ $email = sanitize_email( $_POST['email'] );
348
+ $user = get_user_by( 'email', $email );
349
+
350
+ if ( ! $user instanceof WP_User ) {
351
+ wp_send_json_error();
352
+ }
353
+
354
+ $usermeta = GDPR::get_user_meta( $user->ID );
355
+ $comments = get_comments( array(
356
+ 'author_email' => $user->user_email,
357
+ 'include_unapproved' => true,
358
+ ) );
359
+ $user_consents = get_user_meta( $user->ID, 'gdpr_consents' );
360
+
361
+ ob_start();
362
+ echo '<h2>' . $user->display_name . '<span>( ' . $email . ' )</span></h2>';
363
+ echo '<table class="widefat">
364
+ <tr>
365
+ <td class="row-title">Username</td>
366
+ <td>' . esc_html( $user->user_login ) . '</td>
367
+ </tr>
368
+ <tr>
369
+ <td class="row-title">First Name</td>
370
+ <td>' . esc_html( $user->first_name ) . '</td>
371
+ </tr>
372
+ <tr>
373
+ <td class="row-title">Last Name</td>
374
+ <td>' . esc_html( $user->last_name ) . '</td>
375
+ </tr>
376
+ <tr>
377
+ <td class="row-title">Email</td>
378
+ <td>' . esc_html( $user->user_email ) . '</td>
379
+ </tr>
380
+ <tr>
381
+ <td class="row-title">Nickname</td>
382
+ <td>' . esc_html( $user->nickname ) . '</td>
383
+ </tr>
384
+ <tr>
385
+ <td class="row-title">Bio</td>
386
+ <td>' . esc_html( $user->description ) . '</td>
387
+ </tr>
388
+ <tr>
389
+ <td class="row-title">URL</td>
390
+ <td>' . esc_url( $user->user_url ) . '</td>
391
+ </tr>
392
+ <tr>
393
+ <td class="row-title">Registered</td>
394
+ <td>' . esc_html( $user->user_registered ) . '</td>
395
+ </tr>
396
+ <tr>
397
+ <td class="row-title">Roles</td>
398
+ <td>' . esc_html( implode( ', ', $user->roles ) ) . '</td>
399
+ </tr>
400
+ </table>';
401
+
402
+ if ( ! empty( $user_consents ) ) {
403
+ echo '<h2>' . esc_html__( 'Consent Given', 'gdpr' ) . '</h2>';
404
+ echo '<table class="widefat">
405
+ <thead>
406
+ <tr>
407
+ <th>' . esc_html__( 'Consent ID', 'gdpr' ) . '</th>
408
+ </tr>
409
+ </thead>';
410
+ foreach ( $user_consents as $v ) {
411
+ echo '<tr>';
412
+ echo '<td class="row-title">' . esc_html( $v ) . '</td>';
413
+ echo '</tr>';
414
+ }
415
+ echo '</table>';
416
+ }
417
+
418
+ if ( ! empty( $comments ) ) {
419
+ echo '<h2>' . esc_html__( 'Comments', 'gdpr' ) . '</h2>';
420
+ foreach ( $comments as $v ) {
421
+ echo '<table class="widefat">
422
+ <thead>
423
+ <tr>
424
+ <th class="row-title">' . esc_html__( 'Comment Field', 'gdpr' ) . '</th>
425
+ <th class="row-title">' . esc_html__( 'Comment Data', 'gdpr' ) . '</th>
426
+ </tr>
427
+ </thead>
428
+ <tr>
429
+ <td class="row-title">comment_author</td>
430
+ <td>' . esc_html( $v->comment_author ) . '</td>
431
+ </tr>
432
+ <tr>
433
+ <td class="row-title">comment_author_email</td>
434
+ <td>' . esc_html( $v->comment_author_email ) . '</td>
435
+ </tr>
436
+ <tr>
437
+ <td class="row-title">comment_author_url</td>
438
+ <td>' . esc_html( $v->comment_author_url ) . '</td>
439
+ </tr>
440
+ <tr>
441
+ <td class="row-title">comment_author_IP</td>
442
+ <td>' . esc_html( $v->comment_author_IP ) . '</td>
443
+ </tr>
444
+ <tr>
445
+ <td class="row-title">comment_date</td>
446
+ <td>' . esc_html( $v->comment_date ) . '</td>
447
+ </tr>
448
+ <tr>
449
+ <td class="row-title">comment_agent</td>
450
+ <td>' . esc_html( $v->comment_agent ) . '</td>
451
+ </tr>
452
+ <tr>
453
+ <td class="row-title">comment_content</td>
454
+ <td>' . esc_html( $v->comment_content ) . '</td>
455
+ </tr>
456
+ </table><br>';
457
+ }
458
+ }
459
+
460
+ if ( ! empty( $usermeta ) ) {
461
+ echo '<h2>' . esc_html__( 'Metadata', 'gdpr' ) . '</h2>';
462
+ echo '<table class="widefat">
463
+ <thead>
464
+ <tr>
465
+ <th>' . esc_html__( 'Name', 'gdpr' ) . '</th>
466
+ <th>' . esc_html__( 'Value', 'gdpr' ) . '</th>
467
+ </tr>
468
+ </thead>';
469
+ foreach ( $usermeta as $k => $v ) {
470
+ echo '<tr>';
471
+ echo '<td class="row-title">' . esc_html( $k ) . '</td>';
472
+ echo '<td>';
473
+ foreach ( $v as $value ) {
474
+ if ( is_serialized( $value ) ) {
475
+
476
+ echo '<pre>' . print_r( maybe_unserialize( $value ), true ) . '</pre><br />';
477
+ } else {
478
+ echo print_r( $value, true ) . '<br />';
479
+ }
480
+ }
481
+ echo '</td>';
482
+ echo '</tr>';
483
+ }
484
+ echo '</table>';
485
+
486
+ }
487
+
488
+ do_action( 'admin_access_data_extra_tables', $email );
489
+
490
+ $result = ob_get_clean();
491
+ wp_send_json_success( array( 'user_email' => $email, 'result' => $result ) );
492
+
493
+ }
494
+
495
+ /**
496
+ * The audit-log for the audit log email lookup.
497
+ * @since 1.0.0
498
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
499
+ */
500
+ public function audit_log() {
501
+ if ( ! isset( $_POST['nonce'], $_POST['email'] ) || ! wp_verify_nonce( sanitize_key( wp_unslash( $_POST['nonce'] ) ), 'gdpr-audit-log' ) ) {
502
+ wp_send_json_error();
503
+ }
504
+
505
+ $email = sanitize_email( $_POST['email'] );
506
+ $token = null;
507
+
508
+ if ( isset( $_POST['token'] ) ) {
509
+ $token = sanitize_text_field( wp_unslash( $_POST['token'] ) );
510
+ }
511
+
512
+ $log = GDPR_Audit_log::get_log( $email, $token );
513
+
514
+ if ( ! $log ) {
515
+ wp_send_json_error( esc_html__( 'No logs found for this email.', 'gdpr' ) );
516
+ }
517
+
518
+ wp_send_json_success( $log );
519
+ }
520
+
521
+ /**
522
+ * Admin notice when the user haven't picked a privacy policy page.
523
+ * @since 1.0.0
524
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
525
+ */
526
+ public function privacy_policy_page_missing() {
527
+ $privacy_page = get_option( 'gdpr_privacy_policy_page', '' );
528
+ if ( ! empty( $privacy_page ) ) {
529
+ return;
530
+ }
531
+ ?>
532
+ <div class="notice notice-error is-dismissible">
533
+ <p>
534
+ <strong><?php echo esc_html__( '[GDPR] You must select a Privacy Policy Page.', 'gdpr' ); ?></strong>
535
+ </p>
536
+ <p>
537
+ <a href="<?php echo esc_url( admin_url( 'admin.php?page=gdpr-settings' ) ) ?>" class="button button-primary"><?php esc_html_e( 'Select your Privacy Policy page', 'gdpr' ); ?></a>
538
+ </p>
539
+ </div>
540
+ <?php
541
+ }
542
+
543
+ /**
544
+ * Admin notice when the privacy policy has been updated.
545
+ * @since 1.0.0
546
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
547
+ */
548
+ public function privacy_policy_updated_notice() {
549
+ $updated = get_option( 'gdpr_privacy_policy_updated' );
550
+ if ( ! $updated ) {
551
+ return;
552
+ }
553
+ ?>
554
+ <div class="notice notice-error privacy-page-updated-notice is-dismissible">
555
+ <p>
556
+ <strong><?php echo esc_html__( 'Your Privacy Policy have been updated. In case this was not a small typo fix, you must ask users for explicit consent again.', 'gdpr' ); ?></strong>
557
+ </p>
558
+ <form action="<?php echo esc_url( admin_url('admin-post.php') ); ?>" method="post">
559
+ <?php wp_nonce_field( 'gdpr-seek_consent', 'privacy-policy-updated-nonce' ); ?>
560
+ <input type="hidden" name="action" value="seek_consent">
561
+ <p>
562
+ <?php submit_button( esc_html__( 'Ask for consent', 'gdpr' ), 'primary', 'submit', false ); ?>
563
+ </p>
564
+ </form>
565
+ <form action="<?php echo esc_url( admin_url('admin-post.php') ); ?>" method="post" class="frm-ignore-privacy-update">
566
+ <?php wp_nonce_field( 'gdpr-ignore_update', 'privacy-policy-ignore-update-nonce' ); ?>
567
+ <input type="hidden" name="action" value="ignore_privacy_policy_update">
568
+ <p>
569
+ <?php submit_button( esc_html__( 'Ignore', 'gdpr' ), 'secondary', 'submit', false ); ?>
570
+ </p>
571
+ </form>
572
+ </div>
573
+ <?php
574
+ }
575
+
576
+ /**
577
+ * Sends a confirmation email to the admin email address before continuing with the data breach notification.
578
+ * @since 1.0.0
579
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
580
+ */
581
+ public function send_data_breach_confirmation_email() {
582
+ if ( ! isset( $_POST['gdpr_data_breach_nonce'] ) || ! wp_verify_nonce( sanitize_key( $_POST[ 'gdpr_data_breach_nonce' ] ), 'gdpr-data-breach' ) ) {
583
+ wp_die( esc_html__( 'We could not verify the the security token. Please try again.', 'gdpr' ) );
584
+ }
585
+
586
+ if (
587
+ ! isset(
588
+ $_POST['gdpr-data-breach-email-content'],
589
+ $_POST['gdpr-data-breach-nature'],
590
+ $_POST['gdpr-name-contact-details-protection-officer'],
591
+ $_POST['gdpr-likely-consequences'],
592
+ $_POST['gdpr-measures-taken']
593
+ )
594
+ ) {
595
+ wp_die( esc_html__( 'One or more required fields are missing. Please try again.', 'gdpr' ) );
596
+ }
597
+
598
+ $email = get_bloginfo( 'admin_email' );
599
+ $user = wp_get_current_user();
600
+ $content = sanitize_textarea_field( wp_unslash( $_POST['gdpr-data-breach-email-content'] ) );
601
+ $nature = sanitize_textarea_field( wp_unslash( $_POST['gdpr-data-breach-nature'] ) );
602
+ $office_contact = sanitize_textarea_field( wp_unslash( $_POST['gdpr-name-contact-details-protection-officer'] ) );
603
+ $consequences = sanitize_textarea_field( wp_unslash( $_POST['gdpr-likely-consequences'] ) );
604
+ $measures = sanitize_textarea_field( wp_unslash( $_POST['gdpr-measures-taken'] ) );
605
+
606
+ $key = wp_generate_password( 20, false );
607
+ update_option( 'gdpr_data_breach_initiated', array(
608
+ 'key' => $key,
609
+ 'content' => $content,
610
+ 'nature' => $nature,
611
+ 'office_contact' => $office_contact,
612
+ 'consequences' => $consequences,
613
+ 'measures' => $measures
614
+ ) );
615
+
616
+ $confirm_url = add_query_arg(
617
+ array(
618
+ 'type' => 'data-breach-confirmed',
619
+ 'key' => $key
620
+ ),
621
+ get_home_url() . wp_get_referer() . '#data-breach'
622
+ );
623
+
624
+ GDPR_Email::send(
625
+ $email,
626
+ 'data-breach-request',
627
+ array(
628
+ 'requester' => $user->user_email,
629
+ 'nature'=> $nature,
630
+ 'office_contact' => $office_contact,
631
+ 'consequences' => $consequences,
632
+ 'measures' => $measures,
633
+ 'confirm_url' => $confirm_url,
634
+ )
635
+ );
636
+
637
+ if ( $time = wp_next_scheduled( 'clean_gdpr_data_breach_request' ) ) {
638
+ wp_unschedule_event( $time, 'clean_gdpr_data_breach_request' );
639
+ }
640
+ wp_schedule_single_event( time() + 2 * DAY_IN_SECONDS, 'clean_gdpr_data_breach_request' );
641
+
642
+ add_settings_error( 'gdpr', 'resolved', esc_html__( 'Data breach notification has been initialized. An email confirmation has been sent to the website controller.', 'gdpr' ), 'updated' );
643
+ set_transient( 'settings_errors', get_settings_errors(), 30 );
644
+ wp_safe_redirect(
645
+ esc_url_raw(
646
+ add_query_arg(
647
+ array(
648
+ 'settings-updated' => true
649
+ ),
650
+ wp_get_referer() . '#data-breach'
651
+ )
652
+ )
653
+ );
654
+ exit;
655
+ }
656
+
657
+ /**
658
+ * CRON Job runs this after a couple days to cancel the data breach request.
659
+ * @since 1.0.0
660
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
661
+ */
662
+ public function clean_data_breach_request() {
663
+ delete_option( 'gdpr_data_breach_initiated' );
664
+ }
665
+
666
+ /**
667
+ * CRON job runs this to clean up the telemetry post type every 12 hours.
668
+ * @since 1.0.0
669
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
670
+ */
671
+ public function telemetry_cleanup() {
672
+ $args = array(
673
+ 'post_type' => 'telemetry',
674
+ 'posts_per_page' => -1,
675
+ 'fields' => 'ids',
676
+ );
677
+
678
+ $telemetry_posts = get_posts( $args );
679
+
680
+ foreach ( $telemetry_posts as $post ) {
681
+ wp_delete_post( $post, true );
682
+ }
683
+ }
684
+
685
+ /**
686
+ * Sanitizes the consents during wordpress registration.
687
+ * @since 1.0.0
688
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
689
+ * @param WP_Error $errors The error object.
690
+ * @param string $sanitized_user_login The user login.
691
+ * @param string $user_email The user email.
692
+ * @return WP_Error WP_Error object with added errors or not.
693
+ */
694
+ public function registration_errors( $errors, $sanitized_user_login, $user_email ) {
695
+ $consent_types = get_option( 'gdpr_consent_types', array() );
696
+ if ( empty( $consent_types ) ) {
697
+ return $errors;
698
+ }
699
+
700
+ foreach ( $consent_types as $key => $consent ) {
701
+ if ( $consent['required'] ) {
702
+ if ( ! isset( $_POST['user_consents'][ $key ] ) ) {
703
+ $errors->add( 'missing_required_consents', sprintf(
704
+ '<strong>%s</strong>: %s %s.',
705
+ __( 'ERROR', 'gdpr' ),
706
+ $consent['name'],
707
+ __( 'is a required consent', 'gdpr' )
708
+ ) );
709
+ }
710
+ }
711
+ }
712
+ return $errors;
713
+ }
714
+
715
+ /**
716
+ * Remove the Privacy Policy consent from all users. On next login they will need to consent again.
717
+ * @since 1.0.0
718
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
719
+ */
720
+ public function seek_consent() {
721
+ if ( ! isset( $_POST['privacy-policy-updated-nonce'] ) || ! wp_verify_nonce( sanitize_key( $_POST['privacy-policy-updated-nonce'] ), 'gdpr-seek_consent' ) ) {
722
+ wp_die( esc_html__( 'We could not verify the the security token. Please try again.', 'gdpr' ) );
723
+ }
724
+
725
+ delete_option( 'gdpr_privacy_policy_updated' );
726
+
727
+ $users = get_users( array(
728
+ 'fields' => 'all_with_meta'
729
+ ) );
730
+
731
+ foreach ( $users as $user ) {
732
+ $usermeta = get_user_meta( $user->ID, 'gdpr_consents' );
733
+ if ( in_array( 'privacy-policy', $usermeta ) ) {
734
+ GDPR_Audit_Log::log( $user->ID, esc_html__( 'Privacy Policy has been updated. Removing the Privacy Policy consent and requesting new consent.', 'gdpr' ) );
735
+ delete_user_meta( $user->ID, 'gdpr_consents', 'privacy-policy' );
736
+ }
737
+ }
738
+
739
+ add_settings_error( 'gdpr', 'resolved', esc_html__( 'Users will have to consent to the updated privacy policy on login.', 'gdpr' ), 'updated' );
740
+ set_transient( 'settings_errors', get_settings_errors(), 30 );
741
+ wp_safe_redirect(
742
+ esc_url_raw(
743
+ add_query_arg(
744
+ array(
745
+ 'settings-updated' => true
746
+ ),
747
+ wp_get_referer()
748
+ )
749
+ )
750
+ );
751
+ exit;
752
+ }
753
+
754
+ /**
755
+ * Check if the privacy policy page content has been updated or not.
756
+ * @since 1.0.0
757
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
758
+ * @param int $ID The page ID.
759
+ * @param WP_Post $post The post object.
760
+ */
761
+ public function privacy_policy_updated( $ID, $post ) {
762
+ $privacy_page = (int) get_option( 'gdpr_privacy_policy_page', 0 );
763
+ $ID = (int) $ID;
764
+ if ( $ID === $privacy_page ) {
765
+ $revisions = wp_get_post_revisions( $ID );
766
+ $revisions = array_filter( $revisions, function( $rev ) {
767
+ return strpos( $rev->post_name, 'autosave' ) === false;
768
+ });
769
+
770
+ reset( $revisions );
771
+ if ( current( $revisions )->post_content !== $post->post_content ) {
772
+ update_option( 'gdpr_privacy_policy_updated', 1 );
773
+ }
774
+ }
775
+ }
776
+
777
+ /**
778
+ * Ignore the privacy policy update. The update was probably just a typo fix.
779
+ * @since 1.0.0
780
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
781
+ */
782
+ public function ignore_privacy_policy_update() {
783
+ if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_key( $_POST['nonce'] ), 'gdpr-ignore_update' ) ) {
784
+ wp_send_json_error( esc_html__( 'We could not verify the the security token. Please try again.', 'gdpr' ) );
785
+ }
786
+
787
+ delete_option( 'gdpr_privacy_policy_updated' );
788
+ wp_send_json_success();
789
+ }
790
+
791
+ /**
792
+ * Add consent checkboxes to the user profile on wp dashboard.
793
+ * @since 1.0.0
794
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
795
+ * @param WP_User $user The user object.
796
+ */
797
+ public function edit_user_profile( $user ) {
798
+ $consent_types = get_option( 'gdpr_consent_types', array() );
799
+ $user_consents = get_user_meta( $user->ID, 'gdpr_consents' );
800
+ if ( empty( $consent_types ) ) {
801
+ return;
802
+ }
803
+ ?>
804
+ <h3><?php _e( 'Consent Management', 'gdpr' ); ?></h3>
805
+
806
+ <table class="form-table">
807
+ <?php foreach ( $consent_types as $consent_key => $consent ): ?>
808
+ <tr>
809
+ <th>
810
+ <label><?php echo esc_html( $consent['name'] ); ?></label>
811
+ </th>
812
+ <td>
813
+ <?php if ( $consent['required'] ): ?>
814
+ <input type="checkbox" name="user_consents[]" value="<?php echo esc_attr( $consent_key ); ?>" disabled checked>
815
+ <input type="hidden" name="user_consents[]" value="<?php echo esc_attr( $consent_key ); ?>">
816
+ <?php else: ?>
817
+ <input type="checkbox" name="user_consents[]" value="<?php echo esc_attr( $consent_key ); ?>" <?php echo ! empty( $user_consents ) ? checked( in_array( $consent_key, $user_consents, true ), 1, false ) : ''; ?>>
818
+ <?php endif ?>
819
+ <span class="description"><?php echo wp_kses( $consent['description'], $this->allowed_html ); ?></span>
820
+ </td>
821
+ </tr>
822
+ <?php endforeach ?>
823
+ </table>
824
+
825
+ <?php
826
+ }
827
+
828
+ /**
829
+ * Save the user consent preferences when he update his profile on wp dashboard.
830
+ * @since 1.0.0
831
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
832
+ * @param int $user_id The user ID.
833
+ */
834
+ public function user_profile_update( $user_id ) {
835
+ if ( ! isset( $_POST['user_consents'] ) ) {
836
+ return;
837
+ }
838
+
839
+ $consents = array_map( 'sanitize_text_field', (array) $_POST['user_consents'] );
840
+
841
+ GDPR_Audit_Log::log( $user_id, esc_html__( 'Profile Updated. These are the user consents after the save:', 'gdpr' ) );
842
+
843
+ delete_user_meta( $user_id, 'gdpr_consents' );
844
+
845
+ foreach ( (array) $consents as $consent ) {
846
+ $consent = sanitize_text_field( wp_unslash( $consent ) );
847
+ add_user_meta( $user_id, 'gdpr_consents', $consent );
848
+ GDPR_Audit_Log::log( $user_id, $consent );
849
+ }
850
+
851
+ setcookie( "gdpr[consent_types]", json_encode( $consents ), time() + YEAR_IN_SECONDS, "/" );
852
+ }
853
+
854
+ /**
855
+ * Add the consent checkboxes to the checkout page.
856
+ * @since 1.3.0
857
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
858
+ * @param int $fields The checkout fields.
859
+ */
860
+ public function woocommerce_consent_checkboxes( $fields ) {
861
+ $consent_types = get_option( 'gdpr_consent_types', array() );
862
+
863
+ foreach ( $consent_types as $key => $consent ) {
864
+ $required = ( isset( $consent['required'] ) && $consent['required'] ) ? 'required' : '';
865
+
866
+ $fields['account']['user_consents_' . esc_attr( $key ) ] = array(
867
+ 'type' => 'checkbox',
868
+ 'label' => wp_kses( $consent['registration'], $this->allowed_html ),
869
+ 'required' => $required,
870
+ );
871
+ }
872
+ return $fields;
873
+ }
874
+
875
+ /**
876
+ * Save the user consent when registering from the checkout page.
877
+ * @since 1.3.0
878
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
879
+ * @param int $customer_id The user ID.
880
+ * @param array $data All data submitted during checkout.
881
+ */
882
+ public function woocommerce_checkout_save_consent( $customer_id, $data ) {
883
+ $data = array_filter( $data );
884
+ $consent_arr = array_filter( array_keys( $data ), function( $item ) {
885
+ return false !== strpos( $item, 'user_consents_' );
886
+ } );
887
+
888
+ foreach ( $consent_arr as $key => $value ) {
889
+ $consent = str_replace( 'user_consents_', '', $value );
890
+ add_user_meta( $customer_id, 'gdpr_consents', $consent );
891
+ }
892
+ }
893
+
894
+ }
admin/class-gdpr-requests-admin.php ADDED
@@ -0,0 +1,359 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * The admin facing requests functionality of the plugin.
5
+ *
6
+ * @link https://trewknowledge.com
7
+ * @since 1.0.0
8
+ *
9
+ * @package GDPR
10
+ * @subpackage admin
11
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
12
+ */
13
+
14
+ /**
15
+ * The admin facing requests functionality of the plugin.
16
+ *
17
+ * @package GDPR
18
+ * @subpackage admin
19
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
20
+ */
21
+ class GDPR_Requests_Admin extends GDPR_Requests {
22
+
23
+ /**
24
+ * Add the user to the deletion requests list.
25
+ * @since 1.0.0
26
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
27
+ */
28
+ public function add_to_deletion_requests() {
29
+ if ( ! isset( $_POST['gdpr_deletion_requests_nonce'] ) || ! wp_verify_nonce( sanitize_key( $_POST['gdpr_deletion_requests_nonce'] ), 'gdpr-add-to-deletion-requests' ) ) {
30
+ wp_die( esc_html__( 'We could not verify the user email or the security token. Please try again.', 'gdpr' ) );
31
+ }
32
+
33
+ $email = sanitize_email( $_POST['user_email'] );
34
+ $user = get_user_by( 'email', $email );
35
+
36
+ if ( ! $user instanceof WP_User ) {
37
+ add_settings_error( 'gdpr-requests', 'invalid-user', esc_html__( 'User not found.', 'gdpr' ), 'error' );
38
+ set_transient( 'settings_errors', get_settings_errors(), 30 );
39
+ wp_safe_redirect(
40
+ esc_url_raw(
41
+ add_query_arg(
42
+ array(
43
+ 'settings-updated' => true
44
+ ),
45
+ wp_get_referer() . '#delete'
46
+ )
47
+ )
48
+ );
49
+ exit;
50
+ } else {
51
+ if ( in_array( 'administrator', $user->roles ) ) {
52
+ $admins_query = new WP_User_Query( array(
53
+ 'role' => 'Administrator'
54
+ ) );
55
+ if ( 1 === $admins_query->get_total() ) {
56
+ /* translators: User email */
57
+ add_settings_error( 'gdpr-requests', 'invalid-request', sprintf( esc_html__( 'User %s is the only admin of the site. It cannot be deleted.', 'gdpr' ), $email ), 'error' );
58
+ set_transient( 'settings_errors', get_settings_errors(), 30 );
59
+ wp_safe_redirect(
60
+ esc_url_raw(
61
+ add_query_arg(
62
+ array(
63
+ 'settings-updated' => true
64
+ ),
65
+ wp_get_referer() . '#delete'
66
+ )
67
+ )
68
+ );
69
+ exit;
70
+ }
71
+ }
72
+ }
73
+
74
+ $requests = ( array ) get_option( 'gdpr_requests', array() );
75
+
76
+ if ( empty( $requests ) ) {
77
+ parent::add_to_requests( $email, 'delete', null, true );
78
+ GDPR_Audit_Log::log( $user->ID, esc_html__( 'User added to the deletion requests list by admin.', 'gdpr' ) );
79
+ /* translators: User email */
80
+ add_settings_error( 'gdpr-requests', 'new-request', sprintf( esc_html__( 'User %s was added to the deletion table.', 'gdpr' ), $email ), 'updated' );
81
+ set_transient( 'settings_errors', get_settings_errors(), 30 );
82
+ wp_safe_redirect(
83
+ esc_url_raw(
84
+ add_query_arg(
85
+ array(
86
+ 'settings-updated' => true
87
+ ),
88
+ wp_get_referer() . '#delete'
89
+ )
90
+ )
91
+ );
92
+ exit;
93
+ }
94
+
95
+ $deletion_requests = array_filter( $requests, function( $arr ) {
96
+ return 'delete' === $arr['type'];
97
+ });
98
+ $user_has_already_requested = array_search( $email, array_column( $deletion_requests, 'email' ) );
99
+
100
+ if ( false !== $user_has_already_requested ) {
101
+ add_settings_error( 'gdpr-requests', 'invalid-user', esc_html__( 'User already placed a deletion request.', 'gdpr' ), 'error' );
102
+ set_transient( 'settings_errors', get_settings_errors(), 30 );
103
+ wp_safe_redirect(
104
+ esc_url_raw(
105
+ add_query_arg(
106
+ array(
107
+ 'settings-updated' => true
108
+ ),
109
+ wp_get_referer() . '#delete'
110
+ )
111
+ )
112
+ );
113
+ exit;
114
+ }
115
+
116
+ parent::add_to_requests( $email, 'delete', null, true );
117
+ GDPR_Audit_Log::log( $user->ID, esc_html__( 'User added to the deletion requests list by admin.', 'gdpr' ) );
118
+ /* translators: User email */
119
+ add_settings_error( 'gdpr-requests', 'new-request', sprintf( esc_html__( 'User %s was added to the deletion table.', 'gdpr' ), $email ), 'updated' );
120
+ set_transient( 'settings_errors', get_settings_errors(), 30 );
121
+ wp_safe_redirect(
122
+ esc_url_raw(
123
+ add_query_arg(
124
+ array(
125
+ 'settings-updated' => true
126
+ ),
127
+ wp_get_referer() . '#delete'
128
+ )
129
+ )
130
+ );
131
+ exit;
132
+ }
133
+
134
+ /**
135
+ * Cancels a request.
136
+ * @since 1.0.0
137
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
138
+ */
139
+ public function cancel_request() {
140
+ if ( ! isset( $_POST['type'] ) ) {
141
+ wp_die( esc_html__( 'We could not verify the type of request you want to cancel.', 'gdpr' ) );
142
+ }
143
+
144
+ $type = sanitize_text_field( trim( strtolower( $_POST['type'] ) ) );
145
+ $allowed_types = parent::get_allowed_types();
146
+
147
+ if ( ! in_array( $type, $allowed_types ) ) {
148
+ /* translators: The type of request */
149
+ wp_die( sprintf( esc_html__( 'Type of request \'%s\' is not an allowed type.', 'gdpr' ), $type ) );
150
+ }
151
+
152
+ $nonce_field = 'gdpr_cancel_' . $type . '_nonce';
153
+
154
+ if ( ! isset( $_POST[ $nonce_field ], $_POST['user_email'], $_POST['index'] ) || ! wp_verify_nonce( sanitize_key( $_POST[ $nonce_field ] ), 'gdpr-request-nonce' ) ) {
155
+ wp_die( esc_html__( 'We could not verify the user email or the security token. Please try again.', 'gdpr' ) );
156
+ }
157
+
158
+ $email = sanitize_email( $_POST['user_email'] );
159
+ $index = sanitize_text_field( wp_unslash( $_POST['index'] ) );
160
+
161
+ parent::remove_from_requests( $index );
162
+ $user = get_user_by( 'email', $email );
163
+ /* translators: The type of request i.e 'delete' */
164
+ GDPR_Audit_Log::log( $user->ID, sprintf( esc_html__( 'User was removed from the %s request list by admin.', 'gdpr' ), $type ) );
165
+
166
+ /* translators: User email */
167
+ add_settings_error( 'gdpr-requests', 'remove-request', sprintf( esc_html__( 'User %s was removed from this request table.', 'gdpr' ), $email ), 'updated' );
168
+ set_transient( 'settings_errors', get_settings_errors(), 30 );
169
+ wp_safe_redirect(
170
+ esc_url_raw(
171
+ add_query_arg(
172
+ array(
173
+ 'settings-updated' => true
174
+ ),
175
+ wp_get_referer() . '#' . $type
176
+ )
177
+ )
178
+ );
179
+ exit;
180
+ }
181
+
182
+ /**
183
+ * Marks a request as resolved and notifies the user.
184
+ * @since 1.0.0
185
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
186
+ */
187
+ public function mark_resolved() {
188
+ if ( ! isset( $_POST['type'] ) ) {
189
+ wp_die( esc_html__( 'We could not verify the type of request you want to cancel.', 'gdpr' ) );
190
+ }
191
+
192
+ $type = sanitize_text_field( trim( strtolower( $_POST['type'] ) ) );
193
+ $allowed_types = parent::get_allowed_types();
194
+
195
+ if ( ! in_array( $type, $allowed_types ) ) {
196
+ /* translators: The type of request i.e. 'delete' */
197
+ wp_die( sprintf( esc_html__( 'Type of request \'%s\' is not an allowed type.', 'gdpr' ), $type ) );
198
+ }
199
+
200
+ $nonce_field = 'gdpr_' . $type . '_mark_resolved_nonce';
201
+
202
+ if ( ! isset( $_POST[ $nonce_field ], $_POST['user_email'], $_POST['index'] ) || ! wp_verify_nonce( sanitize_key( $_POST[ $nonce_field ] ), 'gdpr-mark-as-resolved' ) ) {
203
+ wp_die( esc_html__( 'We could not verify the user email or the security token. Please try again.', 'gdpr' ) );
204
+ }
205
+
206
+ $email = sanitize_email( $_POST['user_email'] );
207
+ $index = sanitize_text_field( $_POST['index'] );
208
+
209
+
210
+ parent::remove_from_requests( $index );
211
+
212
+ GDPR_Email::send( $email, $type . '-resolved' );
213
+
214
+ $user = get_user_by( 'email', $email );
215
+ /* translators: User email */
216
+ GDPR_Audit_Log::log( $user->ID, sprintf( esc_html__( 'User %s request was marked as resolved by admin.', 'gdpr' ), $user->user_email ) );
217
+
218
+ add_settings_error( 'gdpr-requests', 'resolved', sprintf( esc_html__( 'Request was resolved. User %s has been notified.', 'gdpr' ), $email ), 'updated' );
219
+ set_transient( 'settings_errors', get_settings_errors(), 30 );
220
+ wp_safe_redirect(
221
+ esc_url_raw(
222
+ add_query_arg(
223
+ array(
224
+ 'settings-updated' => true
225
+ ),
226
+ wp_get_referer() . '#' . $type
227
+ )
228
+ )
229
+ );
230
+ exit;
231
+ }
232
+
233
+ /**
234
+ * Deletes a user from the admin interface.
235
+ * @since 1.0.0
236
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
237
+ */
238
+ public function delete_user() {
239
+ if ( ! isset( $_POST['gdpr_delete_user'], $_POST['user_email'], $_POST['index'] ) || ! wp_verify_nonce( $_POST['gdpr_delete_user'], 'gdpr-request-delete-user' ) ) {
240
+ wp_die( esc_html__( 'We could not verify the user email or the security token. Please try again.', 'gdpr' ) );
241
+ }
242
+
243
+ $email = sanitize_email( $_POST['user_email'] );
244
+ $user = get_user_by( 'email', $email );
245
+ $index = sanitize_text_field( $_POST['index'] );
246
+ parent::remove_from_requests( $index );
247
+
248
+ $token = GDPR::generate_pin();
249
+ GDPR_Email::send( $user->user_email, 'delete-resolved', array( 'token' => $token ) );
250
+
251
+ GDPR_Audit_Log::log( $user->ID, esc_html__( 'User was removed from the site.', 'gdpr') );
252
+ GDPR_Audit_Log::export_log( $user->ID, $token );
253
+ wp_delete_user( $user->ID );
254
+
255
+ /* translators: User email */
256
+ add_settings_error( 'gdpr-requests', 'new-request', sprintf( esc_html__( 'User %s was deleted from the site.', 'gdpr' ), $email ), 'updated' );
257
+ set_transient( 'settings_errors', get_settings_errors(), 30 );
258
+ wp_safe_redirect(
259
+ esc_url_raw(
260
+ add_query_arg(
261
+ array(
262
+ 'settings-updated' => true
263
+ ),
264
+ wp_get_referer() . '#delete'
265
+ )
266
+ )
267
+ );
268
+ exit;
269
+ }
270
+
271
+ /**
272
+ * Anonymize comments from a user.
273
+ * @since 1.0.0
274
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
275
+ */
276
+ public function anonymize_comments() {
277
+ if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_key( wp_unslash( $_POST['nonce'] ) ), 'gdpr-anonymize-comments-action' ) ) {
278
+ wp_send_json_error( esc_html__( 'We could not verify the security token. Please try again.', 'gdpr' ) );
279
+ }
280
+
281
+ $email = sanitize_email( $_POST['user_email'] );
282
+ $comment_count = ( int ) $_POST['comment_count'];
283
+
284
+ $user = get_user_by( 'email', $email );
285
+ if ( ! $user instanceof WP_User ) {
286
+ wp_send_json_error( esc_html__( 'User not found.', 'gdpr' ) );
287
+ }
288
+
289
+ $comments = get_comments( array(
290
+ 'author_email' => $user->user_email,
291
+ 'include_unapproved' => true,
292
+ 'number' => $comment_count,
293
+ ) );
294
+
295
+ foreach ( $comments as $comment ) {
296
+ $new_comment = array();
297
+ $new_comment['comment_ID'] = $comment->comment_ID;
298
+ $new_comment['comment_author_IP'] = '0.0.0.0';
299
+ $new_comment['comment_author_email'] = '';
300
+ $new_comment['comment_author_url'] = '';
301
+ $new_comment['comment_agent'] = '';
302
+ $new_comment['comment_author'] = esc_html__( 'Guest', 'gdpr' );
303
+ $new_comment['user_id'] = 0;
304
+ wp_update_comment( $new_comment );
305
+ }
306
+ GDPR_Audit_Log::log( $user->ID, esc_html__( 'User comments were anonymized.', 'gdpr' ) );
307
+ wp_send_json_success();
308
+ }
309
+
310
+ /**
311
+ * Reassign content to a different user.
312
+ * @since 1.0.0
313
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
314
+ */
315
+ public function reassign_content() {
316
+ if ( ! isset( $_POST['nonce'] ) || ! wp_verify_nonce( sanitize_key( wp_unslash( $_POST['nonce'] ) ), 'gdpr-reassign-content-action' ) ) {
317
+ wp_send_json_error( esc_html__( 'We could not verify the security token. Please try again.', 'gdpr' ) );
318
+ }
319
+
320
+ if ( ! isset( $_POST['user_email'], $_POST['reassign_to'], $_POST['post_type'], $_POST['post_count'] ) ) {
321
+ wp_send_json_error( esc_html__( 'Essential data missing. Please try again.', 'gdpr' ) );
322
+ }
323
+
324
+ $email = sanitize_email( $_POST['user_email'] );
325
+ $reassign_to = ( int ) $_POST['reassign_to'];
326
+ $post_type = sanitize_text_field( wp_unslash( $_POST['post_type'] ) );
327
+ $post_count = ( int ) $_POST['post_count'];
328
+
329
+ $user = get_user_by( 'email', $email );
330
+ if ( ! $user instanceof WP_User ) {
331
+ wp_send_json_error( esc_html__( 'User not found.', 'gdpr' ) );
332
+ }
333
+
334
+ $args = array(
335
+ 'author' => $user->ID,
336
+ 'post_type' => $post_type,
337
+ 'posts_per_page' => $post_count,
338
+ );
339
+
340
+ $posts = get_posts( $args );
341
+
342
+ if ( ! empty( $posts ) ) {
343
+ foreach ( $posts as $post ) {
344
+ wp_update_post( array(
345
+ 'ID' => $post->ID,
346
+ 'post_author' => $reassign_to,
347
+ ) );
348
+ }
349
+
350
+ $reassign_to_user = get_user_by( 'ID', $reassign_to );
351
+ /* translators: 1: The post type, 2: The user the posts were reassigned to */
352
+ GDPR_Audit_Log::log( $user->ID, sprintf( esc_html__( 'User %s were reassigned to %s.', 'gdpr' ), $post_type, $reassign_to_user->display_name ) );
353
+ wp_send_json_success();
354
+ }
355
+
356
+ wp_send_json_error( esc_html__( 'Something went wrong. Please try again.', 'gdpr' ) );
357
+ }
358
+
359
+ }
admin/class-gdpr-telemetry.php ADDED
@@ -0,0 +1,517 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The telemetry post type registration file.
4
+ *
5
+ * @link https://trewknowledge.com
6
+ * @since 1.0.0
7
+ *
8
+ * @package GDPR
9
+ * @subpackage admin
10
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
11
+ */
12
+
13
+ /**
14
+ * The telemetry post type registration file.
15
+ *
16
+ * Defines the custom post type and edit the look and feel of the page.
17
+ *
18
+ * @package GDPR
19
+ * @subpackage admin
20
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
21
+ */
22
+ class GDPR_Telemetry {
23
+
24
+ /**
25
+ * Registers the telemetry post type.
26
+ * @since 1.0.0
27
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
28
+ */
29
+ public function register_post_type() {
30
+ $telemetry_enabled = get_option( 'gdpr_enable_telemetry_tracker', false );
31
+ if ( ! $telemetry_enabled ) {
32
+ wp_clear_scheduled_hook( 'telemetry_cleanup' );
33
+ return;
34
+ }
35
+
36
+ if ( ! wp_next_scheduled( 'telemetry_cleanup' ) ) {
37
+ wp_schedule_event(
38
+ time(),
39
+ 'hourly',
40
+ 'telemetry_cleanup'
41
+ );
42
+ }
43
+
44
+ register_post_type(
45
+ 'telemetry',
46
+ array(
47
+ 'label' => esc_html__( 'Telemetry', 'gdpr' ),
48
+ 'labels' => array(
49
+ 'not_found' => esc_html__( 'No items found. Future connections will be shown at this place.', 'gdpr' ),
50
+ 'not_found_in_trash' => esc_html__( 'No items found in trash.', 'gdpr' ),
51
+ 'search_items' => esc_html__( 'Search in destination', 'gdpr' ),
52
+ ),
53
+ 'public' => false,
54
+ 'show_ui' => true,
55
+ 'show_in_menu' => false,
56
+ 'show_in_nav_menus' => false,
57
+ 'query_var' => true, // try setting to false
58
+ 'hierarchical' => false,
59
+ 'capability_type' => 'post',
60
+ 'publicly_queryable' => false,
61
+ 'exclude_from_search' => true
62
+ )
63
+ );
64
+ }
65
+
66
+ /**
67
+ * Log the call request.
68
+ * @param object $response The call response.
69
+ * @param [type] $type Context under which the hook is fired.
70
+ * @param [type] $class HTTP transport used.
71
+ * @param [type] $args HTTP request arguments.
72
+ * @param [type] $url The request URL.
73
+ * @since 1.0.0
74
+ */
75
+ public function log_request( $response, $type, $class, $args, $url ) {
76
+ $telemetry_enabled = get_option( 'gdpr_enable_telemetry_tracker', false );
77
+ if ( ! $telemetry_enabled ) {
78
+ return;
79
+ }
80
+ /* Only response type */
81
+ if ( 'response' !== $type ) {
82
+ return false;
83
+ }
84
+
85
+ /* Empty url */
86
+ if ( empty( $url ) ) {
87
+ return false;
88
+ }
89
+
90
+ /* Validate host */
91
+ $host = parse_url( $url, PHP_URL_HOST );
92
+
93
+ if ( ! $host ) {
94
+ return false;
95
+ }
96
+
97
+ /* Backtrace data */
98
+ $backtrace = self::_debug_backtrace();
99
+
100
+ /* No reference file found */
101
+ if ( empty( $backtrace['file'] ) ) {
102
+ return false;
103
+ }
104
+
105
+ /* Show your face, file */
106
+ $meta = self::_face_detect( $backtrace['file'] );
107
+
108
+ /* Extract backtrace data */
109
+ $file = str_replace( ABSPATH, '', $backtrace['file'] );
110
+ $line = ( int ) $backtrace['line'];
111
+
112
+ /* Response code */
113
+ $code = ( is_wp_error( $response ) ? -1 : wp_remote_retrieve_response_code( $response ) );
114
+
115
+ $postdata = self::_get_postdata( $args );
116
+
117
+ if ( ! $postdata ) {
118
+ return false;
119
+ }
120
+
121
+ /* Insert CPT */
122
+ $this->insert_post( array(
123
+ 'url' => esc_url_raw($url),
124
+ 'code' => $code,
125
+ 'host' => $host,
126
+ 'file' => $file,
127
+ 'line' => $line,
128
+ 'meta' => $meta,
129
+ 'postdata' => $postdata,
130
+ ) );
131
+ }
132
+
133
+ /**
134
+ * Insert the telemetry post.
135
+ * @since 1.0.0
136
+ * @access private
137
+ * @param array $meta Meta values.
138
+ * @return int The post ID.
139
+ */
140
+ private function insert_post( $meta ) {
141
+ /* Empty? */
142
+ if ( empty( $meta ) ) {
143
+ return;
144
+ }
145
+
146
+ /* Create post */
147
+ $post_id = wp_insert_post(
148
+ array(
149
+ 'post_status' => 'publish',
150
+ 'post_type' => 'telemetry'
151
+ )
152
+ );
153
+
154
+ /* Add meta values */
155
+ foreach( (array) $meta as $key => $value ) {
156
+ add_post_meta( $post_id, '_gdpr_telemetry_' .$key, $value, true );
157
+ }
158
+
159
+ return $post_id;
160
+ }
161
+
162
+ /**
163
+ * Add a Delete All button on top of the table.
164
+ * @param string $post_type The post type.
165
+ * @static
166
+ * @since 1.0.0
167
+ */
168
+ public static function actions_above_table( $post_type ) {
169
+ if ( 'telemetry' !== $post_type ) {
170
+ return;
171
+ }
172
+
173
+ $url = wp_nonce_url(
174
+ add_query_arg(
175
+ array(
176
+ 'action' => 'delete_all',
177
+ 'post_type' => 'telemetry',
178
+ 'post_status' => 'publish'
179
+ ),
180
+ admin_url('edit.php')
181
+ ),
182
+ 'bulk-posts'
183
+ );
184
+ ?>
185
+ <a href="<?php echo esc_url( $url ); ?>" class="button"><?php echo esc_html__('Delete all', 'gdpr'); ?></a>
186
+ <?php
187
+ }
188
+
189
+ /**
190
+ * Adding custom columns.
191
+ * @since 1.0.0
192
+ * @param array $columns The columns array.
193
+ * @return array The new columns.
194
+ */
195
+ public function manage_columns( $columns ) {
196
+ return array(
197
+ 'url' => esc_html__( 'Destination', 'gdpr' ),
198
+ 'file' => esc_html__( 'File', 'gdpr' ),
199
+ 'code' => esc_html__( 'Code', 'gdpr' ),
200
+ 'created' => esc_html__( 'Time', 'gdpr' ),
201
+ 'postdata' => esc_html__( 'Data', 'gdpr')
202
+ );
203
+ }
204
+
205
+ /**
206
+ * Custom columns hook.
207
+ * @since 1.0.0
208
+ * @static
209
+ * @param string $column The column ID.
210
+ * @param int $post_id The post ID.
211
+ */
212
+ public static function custom_column( $column, $post_id ) {
213
+ /* Column types */
214
+ $types = array(
215
+ 'url' => array( __CLASS__, '_html_url' ),
216
+ 'file' => array( __CLASS__, '_html_file' ),
217
+ 'code' => array( __CLASS__, '_html_code' ),
218
+ 'created' => array( __CLASS__, '_html_created' ),
219
+ 'postdata' => array( __CLASS__, '_html_postdata' )
220
+ );
221
+
222
+ /* If type exists */
223
+ if ( ! empty( $types[ $column ] ) ) {
224
+ /* Callback */
225
+ $callback = $types[ $column ];
226
+
227
+ /* Execute */
228
+ if ( is_callable( $callback ) ) {
229
+ call_user_func( $callback, $post_id );
230
+ }
231
+ }
232
+ }
233
+
234
+ /**
235
+ * The URL column callback.
236
+ * @since 1.0.0
237
+ * @static
238
+ * @access private
239
+ * @param int $post_id The post ID.
240
+ */
241
+ private static function _html_url( $post_id ) {
242
+ /* Init data */
243
+ $url = self::_get_post_meta( $post_id, 'url' );
244
+ $host = self::_get_post_meta( $post_id, 'host' );
245
+
246
+ /* Print output */
247
+ echo sprintf(
248
+ '<div>%s</div>',
249
+ str_replace( $host, '<code>' .$host. '</code>', esc_url( $url ) )
250
+ );
251
+ }
252
+
253
+ /**
254
+ * The file column callback.
255
+ * @since 1.0.0
256
+ * @access private
257
+ * @static
258
+ * @param int $post_id The post ID.
259
+ */
260
+ private static function _html_file( $post_id ) {
261
+ $file = self::_get_post_meta( $post_id, 'file' );
262
+ $line = self::_get_post_meta( $post_id, 'line' );
263
+ $meta = self::_get_post_meta( $post_id, 'meta' );
264
+
265
+ /* Print output */
266
+ echo sprintf(
267
+ '<div>%s: %s<br /><code>/%s:%d</code></div>',
268
+ $meta['type'],
269
+ $meta['name'],
270
+ $file,
271
+ $line
272
+ );
273
+ }
274
+
275
+ /**
276
+ * The response code column callback.
277
+ * @since 1.0.0
278
+ * @access private
279
+ * @static
280
+ * @param int $post_id The post ID.
281
+ */
282
+ private static function _html_code( $post_id ) {
283
+ echo self::_get_post_meta( $post_id, 'code' );
284
+ }
285
+
286
+ /**
287
+ * The created column callback.
288
+ * @since 1.0.0
289
+ * @access private
290
+ * @static
291
+ * @param int $post_id The post ID.
292
+ */
293
+ private static function _html_created( $post_id ) {
294
+ /* translators: Amount of time */
295
+ echo sprintf(
296
+ esc_html__( '%s ago' ),
297
+ human_time_diff( get_post_time( 'G', true, $post_id ) )
298
+ );
299
+ }
300
+
301
+ /**
302
+ * The post data column callback.
303
+ * @since 1.0.0
304
+ * @access private
305
+ * @static
306
+ * @param int $post_id The post ID.
307
+ */
308
+ private static function _html_postdata( $post_id ) {
309
+ /* Item post data */
310
+ $postdata = self::_get_post_meta( $post_id, 'postdata' );
311
+
312
+ /* Empty data? */
313
+ if ( empty( $postdata ) ) {
314
+ return;
315
+ }
316
+
317
+ /* Parse POST data */
318
+ if ( ! is_array( $postdata ) ) {
319
+ wp_parse_str( $postdata, $postdata );
320
+ }
321
+
322
+ /* Empty array? */
323
+ if ( empty( $postdata ) ) {
324
+ return;
325
+ }
326
+
327
+ /* Thickbox content start */
328
+ echo sprintf(
329
+ '<div id="gdpr-telemetry-thickbox-%d" class="gdpr-hidden"><pre>',
330
+ $post_id
331
+ );
332
+
333
+ /* POST data */
334
+ print_r( $postdata );
335
+
336
+ /* Thickbox content end */
337
+ echo '</pre></div>';
338
+
339
+ /* Thickbox button */
340
+ echo sprintf(
341
+ '<a href="#TB_inline?width=400&height=300&inlineId=gdpr-telemetry-thickbox-%d" class="button thickbox">%s</a>',
342
+ $post_id,
343
+ esc_html__( 'Show', 'gdpr' )
344
+ );
345
+ }
346
+
347
+ /**
348
+ * Get the post meta we care about.
349
+ * @since 1.0.0
350
+ * @access private
351
+ * @static
352
+ * @param int $post_id The post ID.
353
+ * @param string $key The key that matters to us.
354
+ * @return mixed The post meta.
355
+ */
356
+ private static function _get_post_meta( $post_id, $key ) {
357
+ if ( $value = get_post_meta( $post_id, '_gdpr_telemetry_' .$key, true ) ) {
358
+ return $value;
359
+ }
360
+
361
+ return get_post_meta( $post_id, $key, true );
362
+ }
363
+
364
+ /**
365
+ * The debug backtrace of the call. This gives us the file and line of origin of the call.
366
+ * @since 1.0.0
367
+ * @access private
368
+ * @static
369
+ * @return array Extra information about the call like File and Line.
370
+ */
371
+ private static function _debug_backtrace() {
372
+ /* Reverse items */
373
+ $trace = array_reverse( debug_backtrace() );
374
+
375
+ /* Loop items */
376
+ foreach( $trace as $index => $item ) {
377
+ if ( ! empty( $item['function'] ) && strpos( $item['function'], 'wp_remote_' ) !== false ) {
378
+ /* Use prev item */
379
+ if ( empty( $item['file'] ) ) {
380
+ $item = $trace[-- $index];
381
+ }
382
+
383
+ /* Get file and line */
384
+ if ( ! empty( $item['file'] ) && ! empty( $item['line'] ) ) {
385
+ return $item;
386
+ }
387
+ }
388
+ }
389
+ }
390
+
391
+ /**
392
+ * Is the call coming from a theme or plugin?
393
+ * @since 1.0.0
394
+ * @access private
395
+ * @static
396
+ * @param string $path Path to the file.
397
+ * @return array The name of the plugin or theme that made the call.
398
+ */
399
+ private static function _face_detect( $path ) {
400
+ /* Default */
401
+ $meta = array(
402
+ 'type' => 'WordPress',
403
+ 'name' => 'Core'
404
+ );
405
+
406
+ /* Empty path */
407
+ if ( empty( $path ) ) {
408
+ return $meta;
409
+ }
410
+
411
+ /* Search for plugin */
412
+ if ( $data = self::_localize_plugin( $path ) ) {
413
+ return array(
414
+ 'type' => 'Plugin',
415
+ 'name' => $data['Name'],
416
+ );
417
+
418
+ /* Search for theme */
419
+ } else if ( $data = self::_localize_theme( $path ) ) {
420
+ return array(
421
+ 'type' => 'Theme',
422
+ 'name' => $data->get( 'Name' ),
423
+ );
424
+ }
425
+
426
+ return $meta;
427
+ }
428
+
429
+ /**
430
+ * Figures out if the file that made the call belongs to a plugin.
431
+ * @since 1.0.0
432
+ * @access private
433
+ * @static
434
+ * @param string $path The path to the file that made the call.
435
+ * @return string The plugin name.
436
+ */
437
+ private static function _localize_plugin( $path ) {
438
+ /* Check path */
439
+ if ( false === strpos( $path, WP_PLUGIN_DIR ) ) {
440
+ return false;
441
+ }
442
+
443
+ /* Reduce path */
444
+ $path = ltrim( str_replace( WP_PLUGIN_DIR, '', $path ), DIRECTORY_SEPARATOR );
445
+
446
+ /* Get plugin folder */
447
+ $folder = substr( $path, 0, strpos( $path, DIRECTORY_SEPARATOR ) ) . DIRECTORY_SEPARATOR;
448
+
449
+ /* Frontend */
450
+ if ( ! function_exists( 'get_plugins' ) ) {
451
+ require_once( ABSPATH. 'wp-admin/includes/plugin.php' );
452
+ }
453
+
454
+ /* All active plugins */
455
+ $plugins = get_plugins();
456
+
457
+ /* Loop plugins */
458
+ foreach( $plugins as $path => $plugin ) {
459
+ if ( 0 === strpos( $path, $folder ) ) {
460
+ return $plugin;
461
+ }
462
+ }
463
+ }
464
+
465
+ /**
466
+ * Figures out if the file that made the call belongs to a theme.
467
+ * @since 1.0.0
468
+ * @access private
469
+ * @static
470
+ * @param string $path The path to the file that made the call.
471
+ * @return string The theme name.
472
+ */
473
+ private static function _localize_theme( $path ) {
474
+ /* Check path */
475
+ if ( false === strpos( $path, get_theme_root() ) ) {
476
+ return false;
477
+ }
478
+
479
+ /* Reduce path */
480
+ $path = ltrim( str_replace( get_theme_root(), '', $path ), DIRECTORY_SEPARATOR );
481
+
482
+ /* Get theme folder */
483
+ $folder = substr( $path, 0, strpos( $path, DIRECTORY_SEPARATOR ) );
484
+
485
+ /* Get theme */
486
+ $theme = wp_get_theme( $folder );
487
+
488
+ /* Check & return theme */
489
+ if ( $theme->exists() ) {
490
+ return $theme;
491
+ }
492
+
493
+ return false;
494
+ }
495
+
496
+ /**
497
+ * The data that was transmitted.
498
+ * @since 1.0.0
499
+ * @access private
500
+ * @static
501
+ * @param array $args The http call arguments.
502
+ * @return mixed The request body.
503
+ */
504
+ private static function _get_postdata( $args ) {
505
+ /* No POST data? */
506
+ if ( empty( $args['method'] ) OR 'POST' !== $args['method'] ) {
507
+ return NULL;
508
+ }
509
+
510
+ /* No body data? */
511
+ if ( empty( $args['body'] ) ) {
512
+ return NULL;
513
+ }
514
+
515
+ return $args['body'];
516
+ }
517
+ }
admin/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden
admin/partials/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden
admin/partials/requests.php ADDED
@@ -0,0 +1,323 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Provide a admin area view for the plugin
5
+ *
6
+ * This file is used to markup the admin-facing aspects of the plugin.
7
+ *
8
+ * @link https://trewknowledge.com
9
+ * @since 1.0.0
10
+ *
11
+ * @package GDPR
12
+ * @subpackage admin/partials
13
+ */
14
+
15
+ ?>
16
+
17
+ <div class="wrap gdpr">
18
+ <h1><?php esc_html_e( 'Requests', 'gdpr' ); ?></h1>
19
+ <?php settings_errors(); ?>
20
+ <div class="nav-tab-wrapper">
21
+ <?php foreach ( $tabs as $key => $value ) : ?>
22
+ <a href="<?php echo '#' . $key; ?>" class="nav-tab">
23
+ <?php echo esc_html( $value['name'] ); ?>
24
+ <?php if ( $value['count'] ): ?>
25
+ <span class="gdpr-pending-requests-badge"><?php echo esc_html( $value['count'] ); ?></span>
26
+ <?php endif ?>
27
+ </a>
28
+ <?php endforeach; ?>
29
+ </div>
30
+
31
+ <div class="gdpr-tab hidden" data-id="rectify">
32
+ <h2><?php esc_html_e( 'Rectify Data', 'gdpr' ) ?></h2>
33
+ <table class="widefat gdpr-request-table">
34
+ <thead>
35
+ <tr>
36
+ <th><?php esc_html_e( 'Email', 'gdpr' ); ?></th>
37
+ <th class="text-center"><?php esc_html_e( 'Date of Request', 'gdpr' ); ?></th>
38
+ <th class="text-center"><?php esc_html_e( 'Information', 'gdpr' ); ?></th>
39
+ <th class="text-center"><?php esc_html_e( 'Actions', 'gdpr' ); ?></th>
40
+ </tr>
41
+ </thead>
42
+ <tbody>
43
+ <?php if ( isset( $rectify ) && ! empty( $rectify ) ): ?>
44
+ <?php foreach ( $rectify as $i => $request ): ?>
45
+ <tr>
46
+ <td class="row-title"><?php echo esc_html( $request['email'] ); ?></td>
47
+ <td class="text-center"><?php echo esc_html( $request['date'] ); ?></td>
48
+ <td class="text-center"><?php echo wp_kses( wpautop( wp_unslash( $request['data'] ) ), array( 'p' => true, 'br' => true ) ); ?></td>
49
+ <td class="text-center">
50
+ <form class="frm-process-rectification" action="<?php echo esc_url( admin_url('admin-post.php') ); ?>" method="post">
51
+ <?php wp_nonce_field( 'gdpr-request-nonce', 'gdpr_cancel_rectify_nonce' ); ?>
52
+ <input type="hidden" name="action" value="gdpr_cancel_request">
53
+ <input type="hidden" name="type" value="rectify">
54
+ <input type="hidden" name="index" value="<?php echo esc_attr( $i ); ?>">
55
+ <input type="hidden" name="user_email" value="<?php echo esc_attr( $request['email'] ) ?>">
56
+ <?php submit_button( esc_html__( 'Cancel Request', 'gdpr' ), 'delete', '', false ) ?>
57
+ </form>
58
+ <form class="frm-process-rectification" action="<?php echo esc_url( admin_url('admin-post.php') ); ?>" method="post">
59
+ <?php wp_nonce_field( 'gdpr-mark-as-resolved', 'gdpr_rectify_mark_resolved_nonce' ); ?>
60
+ <input type="hidden" name="action" value="gdpr_mark_resolved">
61
+ <input type="hidden" name="type" value="rectify">
62
+ <input type="hidden" name="index" value="<?php echo esc_attr( $i ); ?>">
63
+ <input type="hidden" name="user_email" value="<?php echo esc_attr( $request['email'] ) ?>">
64
+ <?php submit_button( esc_html__( 'Mark as Resolved', 'gdpr' ), 'primary', '', false ) ?>
65
+ </form>
66
+ </td>
67
+ </tr>
68
+ <?php endforeach ?>
69
+ <?php else: ?>
70
+ <tr>
71
+ <td colspan="4" class="text-center">
72
+ <?php esc_html_e( 'No pending requests', 'gdpr' ); ?>
73
+ </td>
74
+ </tr>
75
+ <?php endif ?>
76
+ </tbody>
77
+ <tfoot>
78
+ <tr>
79
+ <th><?php esc_html_e( 'Email', 'gdpr' ); ?></th>
80
+ <th class="text-center"><?php esc_html_e( 'Date of Request', 'gdpr' ); ?></th>
81
+ <th class="text-center"><?php esc_html_e( 'Information', 'gdpr' ); ?></th>
82
+ <th class="text-center"><?php esc_html_e( 'Actions', 'gdpr' ); ?></th>
83
+ </tr>
84
+ </tfoot>
85
+ </table>
86
+ </div>
87
+
88
+ <div class="gdpr-tab hidden" data-id="complaint">
89
+ <h2><?php esc_html_e( 'Complaints', 'gdpr' ) ?></h2>
90
+ <table class="widefat gdpr-request-table">
91
+ <thead>
92
+ <tr>
93
+ <th><?php esc_html_e( 'Email', 'gdpr' ); ?></th>
94
+ <th class="text-center"><?php esc_html_e( 'Date of Complaint', 'gdpr' ); ?></th>
95
+ <th class="text-center"><?php esc_html_e( 'Information', 'gdpr' ); ?></th>
96
+ <th class="text-center"><?php esc_html_e( 'Actions', 'gdpr' ); ?></th>
97
+ </tr>
98
+ </thead>
99
+ <tbody>
100
+ <?php if ( isset( $complaint ) && ! empty( $complaint ) ): ?>
101
+ <?php foreach ( $complaint as $i => $request ): ?>
102
+ <tr>
103
+ <td class="row-title"><?php echo esc_html( $request['email'] ); ?></td>
104
+ <td class="text-center"><?php echo esc_html( $request['date'] ); ?></td>
105
+ <td class="text-center"><?php echo esc_html( wp_unslash( $request['data'] ) ); ?></td>
106
+ <td class="text-center">
107
+ <form class="frm-process-complaint" action="<?php echo esc_url( admin_url('admin-post.php') ); ?>" method="post">
108
+ <?php wp_nonce_field( 'gdpr-request-nonce', 'gdpr_cancel_complaint_nonce' ); ?>
109
+ <input type="hidden" name="action" value="gdpr_cancel_request">
110
+ <input type="hidden" name="type" value="complaint">
111
+ <input type="hidden" name="index" value="<?php echo esc_attr( $i ); ?>">
112
+ <input type="hidden" name="user_email" value="<?php echo esc_attr( $request['email'] ) ?>">
113
+ <?php submit_button( esc_html__( 'Cancel Request', 'gdpr' ), 'delete', '', false ) ?>
114
+ </form>
115
+ <form class="frm-process-complaint" action="<?php echo esc_url( admin_url('admin-post.php') ); ?>" method="post">
116
+ <?php wp_nonce_field( 'gdpr-mark-as-resolved', 'gdpr_complaint_mark_resolved_nonce' ); ?>
117
+ <input type="hidden" name="action" value="gdpr_mark_resolved">
118
+ <input type="hidden" name="type" value="complaint">
119
+ <input type="hidden" name="index" value="<?php echo esc_attr( $i ); ?>">
120
+ <input type="hidden" name="user_email" value="<?php echo esc_attr( $request['email'] ) ?>">
121
+ <?php submit_button( esc_html__( 'Mark as Resolved', 'gdpr' ), 'primary', '', false ) ?>
122
+ </form>
123
+ </td>
124
+ </tr>
125
+ <?php endforeach ?>
126
+ <?php else: ?>
127
+ <tr>
128
+ <td colspan="4" class="text-center">
129
+ <?php esc_html_e( 'No pending requests', 'gdpr' ); ?>
130
+ </td>
131
+ </tr>
132
+ <?php endif ?>
133
+ </tbody>
134
+ <tfoot>
135
+ <tr>
136
+ <th><?php esc_html_e( 'Email', 'gdpr' ); ?></th>
137
+ <th class="text-center"><?php esc_html_e( 'Date of Complaint', 'gdpr' ); ?></th>
138
+ <th class="text-center"><?php esc_html_e( 'Information', 'gdpr' ); ?></th>
139
+ <th class="text-center"><?php esc_html_e( 'Actions', 'gdpr' ); ?></th>
140
+ </tr>
141
+ </tfoot>
142
+ </table>
143
+ </div>
144
+
145
+ <div class="gdpr-tab hidden" data-id="delete">
146
+ <h2><?php esc_html_e( 'Right to erasure', 'gdpr' ) ?></h2>
147
+ <div class="postbox not-full">
148
+ <form class="gdpr-manual-email-lookup" method="post" action="<?php echo esc_url( admin_url('admin-post.php') ); ?>">
149
+ <div class="inside">
150
+ <input type="hidden" name="action" value="gdpr_add_to_deletion_requests">
151
+ <?php wp_nonce_field( 'gdpr-add-to-deletion-requests', 'gdpr_deletion_requests_nonce' ); ?>
152
+ <h4>
153
+ <label for="gdpr-request-email-lookup"><?php esc_html_e( 'Manually add a user', 'gdpr' ); ?></label>
154
+ </h4>
155
+ <input type="email" name="user_email" class="gdpr-request-email-lookup regular-text" placeholder="<?php esc_attr_e( 'email@domain.com', 'gdpr' ); ?>" required>
156
+ <?php submit_button( esc_html__( 'Submit', 'gdpr' ), 'primary', '', false ); ?>
157
+ </div>
158
+ </form>
159
+ </div>
160
+ <table class="widefat gdpr-request-table">
161
+ <thead>
162
+ <tr>
163
+ <th><?php esc_html_e( 'Email', 'gdpr' ); ?></th>
164
+ <th class="text-center"><?php esc_html_e( 'Date of Request', 'gdpr' ); ?></th>
165
+ <th class="text-center"><?php esc_html_e( 'Review', 'gdpr' ); ?></th>
166
+ <th class="text-center"><?php esc_html_e( 'Actions', 'gdpr' ); ?></th>
167
+ </tr>
168
+ </thead>
169
+ <tbody>
170
+ <?php if ( isset( $delete ) && ! empty( $delete ) ): ?>
171
+ <?php $index = 0; ?>
172
+ <?php foreach ( $delete as $i => $request ): ?>
173
+ <?php $user = get_user_by( 'email', $request['email'] ) ?>
174
+ <tr class="<?php echo ( $index % 2 == 0 ? '' : 'alternate' ); ?>">
175
+ <td class="row-title"><?php echo esc_html( $request['email'] ); ?></td>
176
+ <td class="text-center"><?php echo esc_html( $request['date'] ); ?></td>
177
+ <td class="text-center">
178
+ <?php
179
+ if ( GDPR_Requests::user_has_content( $user ) ) {
180
+ echo '<button class="button gdpr-review" data-index="' . esc_attr( $index ) . '">' . esc_html__( 'Review', 'gdpr' ) . '</button>';
181
+ } else {
182
+ esc_html_e( 'No content to review', 'gdpr' );
183
+ }
184
+ ?>
185
+ <?php if ( GDPR_Requests::user_has_content( $user ) ): ?>
186
+ <?php else: ?>
187
+ <?php ?>
188
+ <?php endif; ?>
189
+ </td>
190
+ <td class="text-center">
191
+ <form class="frm-process-user-deletion" action="<?php echo esc_url( admin_url('admin-post.php') ); ?>" method="post">
192
+ <?php wp_nonce_field( 'gdpr-request-nonce', 'gdpr_cancel_delete_nonce' ); ?>
193
+ <input type="hidden" name="action" value="gdpr_cancel_request">
194
+ <input type="hidden" name="type" value="delete">
195
+ <input type="hidden" name="user_email" value="<?php echo esc_attr( $request['email'] ) ?>">
196
+ <input type="hidden" name="index" value="<?php echo esc_attr( $i ); ?>">
197
+ <?php submit_button( esc_html__( 'Cancel Request', 'gdpr' ), 'delete', '', false ) ?>
198
+ </form>
199
+ <form class="frm-process-user-deletion" action="<?php echo esc_url( admin_url('admin-post.php') ); ?>" method="post">
200
+ <?php wp_nonce_field( 'gdpr-request-delete-user', 'gdpr_delete_user' ); ?>
201
+ <input type="hidden" name="action" value="gdpr_delete_user">
202
+ <input type="hidden" name="user_email" value="<?php echo esc_attr( $request['email'] ) ?>">
203
+ <input type="hidden" name="index" value="<?php echo esc_attr( $i ); ?>">
204
+ <?php submit_button( esc_html__( 'Delete User', 'gdpr' ), 'primary', '', false ) ?>
205
+ </form>
206
+ </td>
207
+ </tr>
208
+ <?php if ( GDPR_Requests::user_has_content( $user ) ): ?>
209
+ <tr class="review" data-index="<?php echo esc_attr( $index ); ?>">
210
+ <td colspan="4">
211
+ <div class="hidden">
212
+ <table class="widefat">
213
+ <thead>
214
+ <tr>
215
+ <th><?php esc_html_e( 'Content Type', 'gdpr' ); ?></th>
216
+ <th class="text-center"><?php _e( 'Count', 'gdpr' ); ?></th>
217
+ <th class="text-center"><?php _e( 'Review', 'gdpr' ); ?></th>
218
+ <th class="text-center"><?php _e( 'Reassign', 'gdpr' ); ?></th>
219
+ <th class="text-center"><?php _e( 'Action', 'gdpr' ); ?></th>
220
+ </tr>
221
+ </thead>
222
+ <tbody>
223
+ <?php $post_types = get_post_types( array( 'public' => true ), 'objects' ); ?>
224
+ <?php foreach ( $post_types as $pt ): ?>
225
+ <?php
226
+ $uid = get_user_by( 'email', $request['email'] );
227
+ if ( $uid && $uid instanceof WP_User ) {
228
+ $uid = $uid->ID;
229
+ }
230
+ $count = count_user_posts( $uid, $pt->name );
231
+ if ( '0' === $count) {
232
+ continue;
233
+ }
234
+ ?>
235
+ <tr>
236
+ <td class="row-title"><?php echo esc_attr( $pt->label ) ?></td>
237
+ <td class="text-center"><?php echo esc_attr( $count ) ?></td>
238
+ <td class="text-center">
239
+ <a href="<?php echo admin_url('edit.php?post_type=' . $pt->name . '&author=' . $uid); ?>" target="_blank" class="button"><?php echo esc_html( $pt->labels->view_items ); ?></a>
240
+ </td>
241
+ <td class="text-center">
242
+ <select name="reassign" class="gdpr-reassign">
243
+ <option value="0"></option>
244
+ <?php $admins = get_users( array( 'role' => 'administrator' ) ); ?>
245
+ <?php foreach ( $admins as $admin ): ?>
246
+ <option value="<?php echo esc_attr( $admin->ID ) ?>"><?php echo esc_html( $admin->display_name ) ?></option>
247
+ <?php endforeach; ?>
248
+ </select>
249
+ </td>
250
+ <td class="text-center">
251
+ <form method="post" class="gdpr-reassign-content">
252
+ <?php wp_nonce_field( 'gdpr-reassign-content-action', 'gdpr_reassign_content_nonce' ) ?>
253
+ <input type="hidden" name="user_email" value="<?php echo esc_attr( $request['email'] ) ?>">
254
+ <input type="hidden" name="reassign_to" value="">
255
+ <input type="hidden" name="post_type" value="<?php echo esc_attr( $pt->name ); ?>">
256
+ <input type="hidden" name="post_count" value="<?php echo esc_attr( $count ); ?>">
257
+ <?php submit_button( esc_html__( 'Reassign', 'gdpr' ), 'primary', '', false, array( 'disabled' => true ) ); ?>
258
+ <span class="spinner"></span>
259
+ <p class="hidden"><strong><?php esc_html_e( 'Resolved', 'gdpr' ); ?></strong></p>
260
+ </form>
261
+ <span class="spinner"></span>
262
+ </td>
263
+ </tr>
264
+ <?php endforeach; ?>
265
+ <?php
266
+ $comment_count = get_comments( array(
267
+ 'author_email' => $request['email'],
268
+ 'include_unapproved' => true,
269
+ 'count' => true,
270
+ ) );
271
+
272
+ if ( $comment_count ) {
273
+ ?>
274
+ <tr>
275
+ <td class="row-title"><?php esc_html_e( 'Comments', 'gdpr' ); ?></td>
276
+ <td class="text-center"><?php echo esc_html( $comment_count ); ?></td>
277
+ <td class="text-center"><a href="<?php echo admin_url( 'edit-comments.php?comment_status=all&s=' . urlencode( $request['email'] ) ); ?>" target="_blank" class="button"><?php _e( 'View Comments', 'gdpr' ); ?></a></td>
278
+ <td></td>
279
+ <td class="text-center">
280
+ <form method="post" class="gdpr-anonymize-comments">
281
+ <?php wp_nonce_field( 'gdpr-anonymize-comments-action', 'gdpr_anonymize_comments_nonce' ) ?>
282
+ <input type="hidden" name="user_email" value="<?php echo esc_attr( $request['email'] ) ?>">
283
+ <input type="hidden" name="comment_count" value="<?php echo esc_attr( $comment_count ) ?>">
284
+ <?php submit_button( esc_html__( 'Anonymize', 'gdpr' ), 'primary', '', false ) ?>
285
+ <span class="spinner"></span>
286
+ <p class="hidden"><strong><?php esc_html_e( 'Resolved', 'gdpr' ); ?></strong></p>
287
+ </form>
288
+ </td>
289
+ </tr>
290
+ <?php
291
+ }
292
+ ?>
293
+ </tbody>
294
+ <tr>
295
+ </tr>
296
+ </table>
297
+ </div>
298
+ </td>
299
+ </tr>
300
+ <?php endif ?>
301
+ <?php $index++; ?>
302
+ <?php endforeach; ?>
303
+ <?php else: ?>
304
+ <tr>
305
+ <td colspan="4" class="text-center">
306
+ <?php esc_html_e( 'No pending requests', 'gdpr' ); ?>
307
+ </td>
308
+ </tr>
309
+ <?php endif ?>
310
+ </tbody>
311
+ <tfoot>
312
+ <tr>
313
+ <th><?php esc_html_e( 'Email', 'gdpr' ); ?></th>
314
+ <th class="text-center"><?php esc_html_e( 'Date of Request', 'gdpr' ); ?></th>
315
+ <th class="text-center"><?php esc_html_e( 'Review', 'gdpr' ); ?></th>
316
+ <th class="text-center"><?php esc_html_e( 'Actions', 'gdpr' ); ?></th>
317
+ </tr>
318
+ </tfoot>
319
+ </table>
320
+ </div>
321
+
322
+ <!-- #poststuff -->
323
+ </div>
admin/partials/settings.php ADDED
@@ -0,0 +1,324 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="wrap gdpr">
2
+ <h1><?php esc_html_e( 'Settings', 'gdpr' ); ?></h1>
3
+ <div class="nav-tab-wrapper">
4
+ <?php foreach ( $tabs as $tab => $value ) : ?>
5
+ <a href="<?php echo '#' . $tab; ?>" class="nav-tab">
6
+ <?php echo esc_html( $value ); ?>
7
+ </a>
8
+ <?php endforeach; ?>
9
+ </div>
10
+
11
+ <?php settings_errors(); ?>
12
+
13
+ <form action="options.php" method="post" class="gdpr-settings-form">
14
+
15
+ <?php settings_fields( 'gdpr' ); ?>
16
+
17
+ <div class="gdpr-tab hidden" data-id="general">
18
+ <h2><?php esc_html_e( 'General', 'gdpr' ) ?></h2>
19
+ <table class="form-table" data-id="general">
20
+ <tbody>
21
+ <tr>
22
+ <th scope="row">
23
+ <label for="gdpr_privacy_policy_page"><?php esc_html_e( 'Privacy Policy Page', 'gdpr' ) ?></label>
24
+ </th>
25
+ <td>
26
+ <?php
27
+ $pages = get_pages();
28
+ ?>
29
+ <select name="gdpr_privacy_policy_page" id="gdpr_privacy_policy_page">
30
+ <option value=""><?php esc_html_e( '-- Select --', 'gdpr' ) ?></option>
31
+ <?php foreach ( $pages as $page ): ?>
32
+ <option value="<?php echo esc_attr( $page->ID ) ?>" <?php selected( $privacy_policy_page, $page->ID ); ?>><?php echo esc_html( $page->post_title ); ?></option>
33
+ <?php endforeach ?>
34
+ </select>
35
+ </td>
36
+ </tr>
37
+ <tr>
38
+ <th scope="row">
39
+ <label for="gdpr_email_limit"><?php esc_html_e( 'Outgoing email limitation', 'gdpr' ) ?></label>
40
+ </th>
41
+ <td>
42
+ <?php $limit = get_option( 'gdpr_email_limit', 100 ); ?>
43
+ <input type="number" name="gdpr_email_limit" id="gdpr_email_limit" value="<?php echo esc_attr( $limit ); ?>">
44
+ </td>
45
+ </tr>
46
+ <tr>
47
+ <th scope="row">
48
+ <label for="gdpr_deletion_needs_review"><?php esc_html_e( 'User deletion', 'gdpr' ) ?></label>
49
+ </th>
50
+ <td>
51
+ <?php $needs_review = get_option( 'gdpr_deletion_needs_review', true ); ?>
52
+ <input type="checkbox" name="<?php echo esc_attr( 'gdpr_deletion_needs_review' ); ?>" id="gdpr_deletion_needs_review" value="1" <?php checked( $needs_review, true ); ?>><span class="description"><label for="gdpr_deletion_needs_review"><?php esc_html_e( 'Send all deletion requests to the review table.', 'gdpr' ); ?></label></label>
53
+ </td>
54
+ </tr>
55
+ <tr>
56
+ <th scope="row">
57
+ <label for="gdpr_disable_css"><?php esc_html_e( 'Disable CSS', 'gdpr' ) ?></label>
58
+ </th>
59
+ <td>
60
+ <?php $disable_css = get_option( 'gdpr_disable_css', false ); ?>
61
+ <input type="checkbox" name="<?php echo esc_attr( 'gdpr_disable_css' ); ?>" id="gdpr_disable_css" value="1" <?php checked( $disable_css, true ); ?>><label for="gdpr_disable_css"><span class="description"><?php esc_html_e( 'Make sure you know what you are doing before checking this.', 'gdpr' ); ?></span></label>
62
+ </td>
63
+ </tr>
64
+ <tr>
65
+ <th scope="row">
66
+ <label for="gdpr_enable_telemetry_tracker"><?php esc_html_e( 'Enable the Telemetry Tracker', 'gdpr' ) ?></label>
67
+ </th>
68
+ <td>
69
+ <?php $enable_telemetry = get_option( 'gdpr_enable_telemetry_tracker', false ); ?>
70
+ <input type="checkbox" name="<?php echo esc_attr( 'gdpr_enable_telemetry_tracker' ); ?>" id="gdpr_enable_telemetry_tracker" value="1" <?php checked( $enable_telemetry, true ); ?>><label for="gdpr_enable_telemetry_tracker"><span class="description"><?php esc_html_e( 'Toggles the Telemetry Tracker On/Off. (experimental)', 'gdpr' ); ?></span></label>
71
+ </td>
72
+ </tr>
73
+ </tbody>
74
+ </table>
75
+ <h2 class="title"><?php esc_html_e( 'Privacy Center', 'gdpr' ); ?></h2>
76
+ <p>
77
+ <?php esc_html_e( 'This section handles the privacy bar and some of the privacy preferences window.', 'gdpr' ) ?><br>
78
+ <strong><?php esc_html_e( 'Important:', 'gdpr' ); ?></strong> <?php esc_html_e( 'If the privacy banner text is not filled out, the privacy banner will not show up. Even if you registered your cookies.', 'gdpr' ) ?></p>
79
+ <table class="form-table" data-id="general">
80
+ <tbody>
81
+ <tr>
82
+ <th scope="row">
83
+ <label for="gdpr_cookie_banner_content"><?php esc_html_e( 'Privacy Banner Text', 'gdpr' ) ?></label>
84
+ </th>
85
+ <td>
86
+ <?php $privacy_bar_content = get_option( 'gdpr_cookie_banner_content', '' ); ?>
87
+ <textarea name="gdpr_cookie_banner_content" id="gdpr_cookie_banner_content" cols="80" rows="6"><?php echo esc_html( $privacy_bar_content ); ?></textarea>
88
+ </td>
89
+ </tr>
90
+ <tr>
91
+ <th scope="row">
92
+ <label for="gdpr_cookie_privacy_excerpt"><?php esc_html_e( 'Privacy Excerpt', 'gdpr' ) ?></label>
93
+ </th>
94
+ <td>
95
+ <?php $privacy_excerpt = get_option( 'gdpr_cookie_privacy_excerpt', '' ); ?>
96
+ <textarea name="gdpr_cookie_privacy_excerpt" id="gdpr_cookie_privacy_excerpt" cols="80" rows="6"><?php echo esc_html( $privacy_excerpt ); ?></textarea>
97
+ <p class="description"><?php esc_html_e( 'This will appear in the consent section of the privacy preference window.', 'gdpr' ); ?></p>
98
+ </td>
99
+ </tr>
100
+ </tbody>
101
+ </table>
102
+ <h2 class="title"><?php esc_html_e( 'Request Forms reCAPTCHA', 'gdpr' ); ?></h2>
103
+ <p><?php esc_html_e( 'To prevent spam attacks, you have the option to enable reCAPTCHA. Configure below your keys to make it work with our request forms.', 'gdpr' ); ?></p>
104
+ <p>
105
+ <?php echo sprintf(
106
+ /* translators: External link with instructions on how to proceed. */
107
+ esc_html__( 'You can find the necessary information %s.', 'gdpr' ),
108
+ '<a href="https://www.google.com/recaptcha/admin" target="_blank">' . esc_html__( 'here', 'gdpr' ) . '</a>'
109
+ ) ?></p>
110
+ <table class="form-table" data-id="general">
111
+ <tbody>
112
+ <tr>
113
+ <th scope="row">
114
+ <label for="gdpr_use_recaptcha"><?php esc_html_e( 'Enable reCAPTCHA', 'gdpr' ) ?></label>
115
+ </th>
116
+ <td>
117
+ <?php $use_recaptcha = get_option( 'gdpr_use_recaptcha', false ); ?>
118
+ <input type="checkbox" name="<?php echo esc_attr( 'gdpr_use_recaptcha' ); ?>" id="gdpr_use_recaptcha" value="1" <?php checked( $use_recaptcha, true ); ?>>
119
+ </td>
120
+ </tr>
121
+ <tr>
122
+ <th scope="row">
123
+ <label for="gdpr_recaptcha_site_key"><?php esc_html_e( 'Site Key', 'gdpr' ) ?></label>
124
+ </th>
125
+ <td>
126
+ <?php $site_key = get_option( 'gdpr_recaptcha_site_key', '' ); ?>
127
+ <input type="text" name="gdpr_recaptcha_site_key" value="<?php echo esc_attr( $site_key ); ?>" placeholder="">
128
+ </td>
129
+ </tr>
130
+ <tr>
131
+ <th scope="row">
132
+ <label for="gdpr_recaptcha_secret_key"><?php esc_html_e( 'Secret Key', 'gdpr' ) ?></label>
133
+ </th>
134
+ <td>
135
+ <?php $secret_key = get_option( 'gdpr_recaptcha_secret_key', '' ); ?>
136
+ <input type="password" name="gdpr_recaptcha_secret_key" value="<?php echo esc_attr( $secret_key ); ?>" placeholder="">
137
+ </td>
138
+ </tr>
139
+ </tbody>
140
+ </table>
141
+ <?php if ( class_exists( 'WooCommerce' ) ): ?>
142
+ <h2 class="title"><?php esc_html_e( 'WooCommerce', 'gdpr' ); ?></h2>
143
+ <table class="form-table" data-id="general">
144
+ <tbody>
145
+ <tr>
146
+ <th scope="row">
147
+ <label for="gdpr_add_consent_checkboxes_registration"><?php esc_html_e( 'Add consent checkboxes to the registration page', 'gdpr' ) ?></label>
148
+ </th>
149
+ <td>
150
+ <?php $add_checkboxes_to_registration = get_option( 'gdpr_add_consent_checkboxes_registration', false ); ?>
151
+ <input type="checkbox" name="<?php echo esc_attr( 'gdpr_add_consent_checkboxes_registration' ); ?>" id="gdpr_add_consent_checkboxes_registration" value="1" <?php checked( $add_checkboxes_to_registration, true ); ?>>
152
+ </td>
153
+ </tr>
154
+ <tr>
155
+ <th scope="row">
156
+ <label for="gdpr_add_consent_checkboxes_checkout"><?php esc_html_e( 'Add consent checkboxes to the checkout registration form', 'gdpr' ) ?></label>
157
+ </th>
158
+ <td>
159
+ <?php $add_checkboxes_to_checkout = get_option( 'gdpr_add_consent_checkboxes_checkout', false ); ?>
160
+ <input type="checkbox" name="<?php echo esc_attr( 'gdpr_add_consent_checkboxes_checkout' ); ?>" id="gdpr_add_consent_checkboxes_checkout" value="1" <?php checked( $add_checkboxes_to_checkout, true ); ?>>
161
+ </td>
162
+ </tr>
163
+ </tbody>
164
+ </table>
165
+ <?php endif ?>
166
+ </div>
167
+ <div class="gdpr-tab hidden" data-id="cookies">
168
+ <h2><?php esc_html_e( 'Cookies', 'gdpr' ) ?></h2>
169
+ <input type="text" id="cookie-tabs" class="regular-text" placeholder="<?php esc_attr_e( 'Category name', 'gdpr' ); ?>">
170
+ <button class="button button-primary add-tab"><?php esc_html_e( 'Add tab', 'gdpr' ); ?></button>
171
+ <div id="tabs">
172
+ <?php $cookie_tabs = get_option( 'gdpr_cookie_popup_content', array() ); ?>
173
+ <?php if ( ! empty( $cookie_tabs ) ) : ?>
174
+ <?php foreach ( $cookie_tabs as $tab_key => $tab ) : ?>
175
+ <div class="postbox" id="cookie-tab-content-<?php echo esc_attr( $tab_key ); ?>">
176
+ <h2 class="hndle"><?php echo esc_html( $tab['name'] ); ?><button class="notice-dismiss" type="button"><span class="screen-reader-text"><?php esc_html_e( 'Remove this tab.', 'gdpr' ); ?></span></button></h2>
177
+ <input type="hidden" name="<?php echo esc_attr( 'gdpr_cookie_popup_content' ); ?>[<?php echo esc_attr( $tab_key ); ?>][name]" value="<?php echo esc_attr( $tab['name'] ); ?>" />
178
+ <div class="inside">
179
+ <table class="form-table">
180
+ <tr>
181
+ <th><label for="always-active-<?php echo esc_attr( $tab_key ); ?>"><?php esc_html_e( 'Always active', 'gdpr' ); ?></label></th>
182
+ <td>
183
+ <label class="gdpr-switch">
184
+ <input type="checkbox" name="<?php echo esc_attr( 'gdpr_cookie_popup_content' ); ?>[<?php echo esc_attr( $tab_key ); ?>][always_active]" <?php checked( esc_attr( $tab['always_active'] ), 1 ); ?> id="always-active-<?php echo esc_attr( $tab_key ); ?>">
185
+ <span class="gdpr-slider round"></span>
186
+ </label>
187
+ </td>
188
+ </tr>
189
+ <tr>
190
+ <th><label for="tab-how-we-use-<?php echo esc_attr( $tab_key ); ?>"><?php esc_html_e( 'How we use', 'gdpr' ); ?></label></th>
191
+ <td><textarea name="<?php echo esc_attr( 'gdpr_cookie_popup_content' ); ?>[<?php echo esc_attr( $tab_key ); ?>][how_we_use]" id="tab-how-we-use-<?php echo esc_attr( $tab_key ); ?>" cols="53" rows="3" required><?php echo esc_html( $tab['how_we_use'] ); ?></textarea></td>
192
+ </tr>
193
+ <tr>
194
+ <th><label for="cookies-used-<?php echo esc_attr( $tab_key ); ?>"><?php esc_html_e( 'Cookies used by the site', 'gdpr' ); ?></label></th>
195
+ <td>
196
+ <textarea cols="53" rows="3" name="<?php echo esc_attr( 'gdpr_cookie_popup_content' ); ?>[<?php echo esc_attr( $tab_key ); ?>][cookies_used]" id="cookies-used-<?php echo esc_attr( $tab_key ); ?>" required><?php echo esc_attr( $tab['cookies_used'] ); ?></textarea>
197
+ <br>
198
+ <span class="description"><?php esc_html_e( 'Comma separated list.', 'gdpr' ); ?></span>
199
+ </td>
200
+ </tr>
201
+ <tr>
202
+ <th><label for="hosts-<?php echo esc_attr( $tab_key ); ?>"><?php esc_html_e( 'Third party domains', 'gdpr' ); ?></label></th>
203
+ <td>
204
+ <input type="text" id="hosts-<?php echo esc_attr( $tab_key ); ?>" class="regular-text" placeholder="domain.com" />
205
+ <button class="button button-primary add-host" data-tabid="<?php echo esc_attr( $tab_key ); ?>"><?php esc_html_e( 'Add', 'gdpr' ); ?></button>
206
+ <br>
207
+ <span class="description"><?php esc_html_e( 'Cookies that are set by a third party, like facebook.com.', 'gdpr' ); ?></span>
208
+ </td>
209
+ </tr>
210
+ </table>
211
+ <div class="tab-hosts" data-tabid="<?php echo esc_attr( $tab_key ); ?>">
212
+ <?php if ( isset( $tab['hosts'] ) && $tab['hosts'] ) : ?>
213
+ <?php foreach ( $tab['hosts'] as $host_key => $host ) : ?>
214
+ <div class="postbox">
215
+ <h2 class="hndle"><?php echo esc_attr( $host_key ); ?><button class="notice-dismiss" type="button"><span class="screen-reader-text"><?php esc_html_e( 'Remove this domain.', 'gdpr' ); ?></span></button></h2>
216
+ <input type="hidden" name="<?php echo esc_attr( 'gdpr_cookie_popup_content' ); ?>[<?php echo esc_attr( $tab_key ); ?>][hosts][<?php echo esc_attr( $host_key ); ?>][name]" value="<?php echo esc_attr( $host_key ); ?>" />
217
+ <div class="inside">
218
+ <table class="form-table">
219
+ <tr>
220
+ <th><label for="hosts-cookies-used-<?php echo esc_attr( $host_key ); ?>"><?php esc_html_e( 'Cookies used', 'gdpr' ); ?></label></th>
221
+ <td>
222
+ <textarea cols="53" rows="3" name="<?php echo esc_attr( 'gdpr_cookie_popup_content' ); ?>[<?php echo esc_attr( $tab_key ); ?>][hosts][<?php echo esc_attr( $host_key ); ?>][cookies_used]" id="hosts-cookies-used-<?php echo esc_attr( $host_key ); ?>" required><?php echo esc_attr( $host['cookies_used'] ); ?></textarea>
223
+ <br>
224
+ <span class="description"><?php esc_html_e( 'Comma separated list.', 'gdpr' ); ?></span>
225
+ </td>
226
+ </tr>
227
+ <tr>
228
+ <th><label for="hosts-cookies-optout-<?php echo esc_attr( $host_key ); ?>"><?php esc_html_e( 'How to Opt Out', 'gdpr' ); ?></label></th>
229
+ <td>
230
+ <input type="text" name="<?php echo esc_attr( 'gdpr_cookie_popup_content' ); ?>[<?php echo esc_attr( $tab_key ); ?>][hosts][<?php echo esc_attr( $host_key ); ?>][optout]" value="<?php echo esc_attr( $host['optout'] ); ?>" id="hosts-cookies-optout-<?php echo esc_attr( $host_key ); ?>" class="regular-text" required />
231
+ <br>
232
+ <span class="description"><?php esc_html_e( 'Url with instructions on how to opt out.', 'gdpr' ); ?></span>
233
+ </td>
234
+ </tr>
235
+ </table>
236
+ </div>
237
+ </div>
238
+ <?php endforeach; ?>
239
+ <?php endif; ?>
240
+ </div>
241
+ </div><!-- .inside -->
242
+ </div><!-- .postbox -->
243
+ <?php endforeach ?>
244
+ <?php endif ?>
245
+ </div>
246
+ </div>
247
+ <div class="gdpr-tab hidden" data-id="consents">
248
+ <h2><?php esc_html_e( 'Consents', 'gdpr' ) ?></h2>
249
+ <input type="text" id="type-of-consent" class="regular-text" placeholder="<?php esc_attr_e( 'Type of consent', 'gdpr' ); ?>">
250
+ <button class="button button-primary add-consent"><?php esc_html_e( 'Add consent', 'gdpr' ); ?></button>
251
+ <div id="consent-tabs">
252
+ <?php
253
+ $default_consent_types = array(
254
+ 'privacy-policy' => array(
255
+ 'name' => 'Privacy Policy',
256
+ 'required' => 'on',
257
+ 'description' => sprintf( __( 'You read and agreed to our %s.', 'gdpr' ), '<a href="" target="_blank">' . esc_html( 'Privacy Policy', 'gdpr' ) . '</a>' ),
258
+ 'registration' => sprintf( __( 'You read and agreed to our %s.', 'gdpr' ), '<a href="" target="_blank">' . esc_html( 'Privacy Policy', 'gdpr' ) . '</a>' ),
259
+ )
260
+ );
261
+ $consent_types = get_option( 'gdpr_consent_types', $default_consent_types ); ?>
262
+ <?php if ( ! empty( $consent_types ) ) : ?>
263
+ <?php foreach ( $consent_types as $consent_key => $consent ) : ?>
264
+ <div class="postbox" id="consent-type-content-<?php echo esc_attr( $consent_key ); ?>">
265
+ <h2 class="hndle"><?php echo esc_html( $consent['name'] ); ?> <span>(id: <?php echo esc_html( $consent_key ); ?>)</span><?php echo ( 'privacy-policy' === $consent_key ) ? '' : '<button class="notice-dismiss" type="button"><span class="screen-reader-text">' . esc_html__( 'Unregister this consent.', 'gdpr' ) . '</span></button>'; ?></h2>
266
+ <input type="hidden" name="<?php echo esc_attr( 'gdpr_consent_types' ); ?>[<?php echo esc_attr( $consent_key ); ?>][name]" value="<?php echo esc_attr( $consent['name'] ); ?>" />
267
+ <div class="inside">
268
+ <table class="form-table">
269
+ <tr>
270
+ <th><label for="required-<?php echo esc_attr( $consent_key ); ?>"><?php esc_html_e( 'Required', 'gdpr' ); ?></label></th>
271
+ <td>
272
+ <?php if ( 'privacy-policy' === $consent_key ): ?>
273
+ <span><?php esc_html_e( 'Required', 'gdpr' ) ?></span>
274
+ <input type="hidden" name="<?php echo esc_attr( 'gdpr_consent_types' ); ?>[<?php echo esc_attr( $consent_key ); ?>][required]" id="required-<?php echo esc_attr( $consent_key ); ?>" value="1">
275
+ <?php else: ?>
276
+ <label class="gdpr-switch">
277
+ <input type="checkbox" name="<?php echo esc_attr( 'gdpr_consent_types' ); ?>[<?php echo esc_attr( $consent_key ); ?>][required]" <?php checked( esc_attr( $consent['required'] ), 1 ); ?> id="required-<?php echo esc_attr( $consent_key ); ?>">
278
+ <span class="gdpr-slider round"></span>
279
+ </label>
280
+ <?php endif; ?>
281
+ </td>
282
+ </tr>
283
+ <tr>
284
+ <th><label for="consent-description-<?php echo esc_attr( $consent_key ); ?>"><?php esc_html_e( 'Consent description', 'gdpr' ); ?></label></th>
285
+ <td><textarea name="<?php echo esc_attr( 'gdpr_consent_types' ); ?>[<?php echo esc_attr( $consent_key ); ?>][description]" id="consent-description-<?php echo esc_attr( $consent_key ); ?>" cols="53" rows="3" required><?php echo esc_html( $consent['description'] ); ?></textarea></td>
286
+ </tr>
287
+ <tr>
288
+ <th><label for="consent-registration-<?php echo esc_attr( $consent_key ); ?>"><?php esc_html_e( 'Registration message', 'gdpr' ); ?></label></th>
289
+ <td><textarea name="<?php echo esc_attr( 'gdpr_consent_types' ); ?>[<?php echo esc_attr( $consent_key ); ?>][registration]" id="consent-registration-<?php echo esc_attr( $consent_key ); ?>" cols="53" rows="3" required><?php echo esc_html( $consent['registration'] ); ?></textarea></td>
290
+ </tr>
291
+ </table>
292
+ </div><!-- .inside -->
293
+ </div><!-- .postbox -->
294
+ <?php endforeach ?>
295
+ <?php endif ?>
296
+ </div>
297
+ </div>
298
+ <div class="gdpr-tab hidden" data-id="export-chanel">
299
+ <h2><?php esc_html_e( 'Data Export Chanel', 'gdpr' ) ?></h2>
300
+ <table class="form-table" data-id="general">
301
+ <tbody>
302
+ <tr>
303
+ <th scope="row">
304
+ <label for="data-export-chanel"><?php esc_html_e( 'Data Export Chanel', 'gdpr' ) ?></label>
305
+ </th>
306
+ <td>
307
+ <?php
308
+ $export_data = get_option('gdpr_export_data');
309
+ ?>
310
+ <input name="<?php echo esc_attr( 'gdpr_export_data' ); ?>" type="radio" value="export-data" <?php if ($export_data == 'export-data') { ?> checked <?php } ?> ><?php esc_html_e( 'Data Export with email attachment', 'gdpr' ) ?> <br>
311
+ <input name="<?php echo esc_attr( 'gdpr_export_data' ); ?>" type="radio" value="file-export-data" <?php if ($export_data == 'file-export-data') { ?> checked <?php } ?>><?php esc_html_e( 'Data Export with download link', 'gdpr' ) ?> <br>
312
+ </td>
313
+ </tr>
314
+ </tbody>
315
+ </table>
316
+ </div>
317
+ <?php
318
+ do_action( 'gdpr_extra_settings' );
319
+ submit_button();
320
+ ?>
321
+ </form>
322
+
323
+ <!-- #poststuff -->
324
+ </div>
admin/partials/templates/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden
admin/partials/templates/tmpl-consents.php ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script type="text/html" id="tmpl-consents">
2
+ <div class="postbox" id="consent-type-content-{{data.key}}">
3
+ <h2 class="hndle">{{data.name}} <span>(id: {{data.key}})</span><button class="notice-dismiss" type="button"><span class="screen-reader-text"><?php esc_html_e( 'Unregister this consent.', 'gdpr' ); ?></span></button></h2>
4
+ <input type="hidden" name="{{data.option_name}}[{{data.key}}][name]" value="{{data.name}}" />
5
+ <input type="hidden" name="{{data.option_name}}[{{data.key}}][id]" value="{{data.id}}" />
6
+ <div class="inside">
7
+ <table class="form-table">
8
+ <tr>
9
+ <th><label for="required-{{data.key}}"><?php esc_html_e( 'Required', 'gdpr' ); ?></label></th>
10
+ <td>
11
+ <label class="gdpr-switch">
12
+ <input type="checkbox" name="{{data.option_name}}[{{data.key}}][required]" id="required-{{data.key}}">
13
+ <span class="gdpr-slider round"></span>
14
+ </label>
15
+ </td>
16
+ </tr>
17
+ <tr>
18
+ <th><label for="consent-description-{{data.key}}"><?php esc_html_e( 'Consent description', 'gdpr' ); ?></label></th>
19
+ <td><textarea name="{{data.option_name}}[{{data.key}}][description]" id="consent-description-{{data.key}}" cols="53" rows="3" required></textarea></td>
20
+ </tr>
21
+ <tr>
22
+ <th><label for="consent-registration-{{data.key}}"><?php esc_html_e( 'Registration message', 'gdpr' ); ?></label></th>
23
+ <td><textarea name="{{data.option_name}}[{{data.key}}][registration]" id="consent-registration-{{data.key}}" cols="53" rows="3" required></textarea></td>
24
+ </tr>
25
+ </table>
26
+ </div><!-- .inside -->
27
+ </div><!-- .postbox -->
28
+ </script>
admin/partials/templates/tmpl-cookies.php ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script type="text/html" id="tmpl-cookie-tabs">
2
+ <div class="postbox" id="cookie-tab-content-{{data.key}}">
3
+ <h2 class="hndle">{{data.name}}<button class="notice-dismiss" type="button"><span class="screen-reader-text"><?php esc_html_e( 'Remove this tab.', 'gdpr' ); ?></span></button></h2>
4
+ <input type="hidden" name="{{data.option_name}}[{{data.key}}][name]" value="{{data.name}}" />
5
+ <div class="inside">
6
+ <table class="form-table">
7
+ <tr>
8
+ <th><label for="always-active-{{data.key}}"><?php esc_html_e( 'Always active', 'gdpr' ); ?></label></th>
9
+ <td>
10
+ <label class="gdpr-switch">
11
+ <input type="checkbox" name="{{data.option_name}}[{{data.key}}][always_active]" id="always-active-{{data.key}}">
12
+ <span class="gdpr-slider round"></span>
13
+ </label>
14
+ </td>
15
+ </tr>
16
+ <tr>
17
+ <th><label for="tab-how-we-use-{{data.key}}"><?php esc_html_e( 'How we use', 'gdpr' ); ?></label></th>
18
+ <td><textarea name="{{data.option_name}}[{{data.key}}][how_we_use]" id="tab-how-we-use-{{data.key}}" cols="53" rows="3" required></textarea></td>
19
+ </tr>
20
+ <tr>
21
+ <th><label for="cookies-used-{{data.key}}"><?php esc_html_e( 'Cookies used by the site', 'gdpr' ); ?></label></th>
22
+ <td>
23
+ <textarea cols="53" rows="3" name="{{data.option_name}}[{{data.key}}][cookies_used]" id="cookies-used-{{data.key}}" required></textarea>
24
+ <br>
25
+ <span class="description"><?php esc_html_e( 'Comma separated list.', 'gdpr' ); ?></span>
26
+ </td>
27
+ </tr>
28
+ <tr>
29
+ <th><label for="hosts-{{data.key}}"><?php esc_html_e( 'Third Party Domains', 'gdpr' ); ?></label></th>
30
+ <td>
31
+ <input type="text" id="hosts-{{data.key}}" class="regular-text" placeholder="domain.com" />
32
+ <button class="button button-primary add-host" data-tabid="{{data.key}}"><?php esc_html_e( 'Add', 'gdpr' ); ?></button>
33
+ <br>
34
+ <span class="description"><?php esc_html_e( 'Cookies that are set by a third party, like facebook.com', 'gdpr' ); ?></span>
35
+ </td>
36
+ </tr>
37
+ </table>
38
+ <div class="tab-hosts" data-tabid="{{data.key}}">
39
+
40
+ </div>
41
+ </div><!-- .inside -->
42
+ </div><!-- .postbox -->
43
+ </script>
44
+
45
+
46
+ <script type="text/html" id="tmpl-cookie-tabs-hosts">
47
+ <div class="postbox">
48
+ <h2 class="hndle">{{data.host_key}}<button class="notice-dismiss" type="button"><span class="screen-reader-text"><?php esc_html_e( 'Remove this domain.', 'gdpr' ); ?></span></button></h2>
49
+ <input type="hidden" name="{{data.option_name}}[{{data.tab_key}}][hosts][{{data.host_key}}][name]" value="{{data.host_key}}" />
50
+ <div class="inside">
51
+ <table class="form-table">
52
+ <tr>
53
+ <th><label for="hosts-cookies-used-{{data.host_key}}"><?php esc_html_e( 'Cookies used', 'gdpr' ); ?></label></th>
54
+ <td>
55
+ <textarea cols="53" rows="3" name="{{data.option_name}}[{{data.tab_key}}][hosts][{{data.host_key}}][cookies_used]" id="hosts-cookies-used-{{data.host_key}}" required></textarea>
56
+ <br>
57
+ <span class="description"><?php esc_html_e( 'Comma separated list.', 'gdpr' ); ?></span>
58
+ </td>
59
+ </tr>
60
+ <tr>
61
+ <th><label for="hosts-cookies-optout-{{data.host_key}}"><?php esc_html_e( 'How to Opt Out', 'gdpr' ); ?></label></th>
62
+ <td>
63
+ <input type="text" name="{{data.option_name}}[{{data.tab_key}}][hosts][{{data.host_key}}][optout]" id="hosts-cookies-optout-{{data.host_key}}" class="regular-text" required />
64
+ <br>
65
+ <span class="description"><?php esc_html_e( 'Url with instructions on how to opt out.', 'gdpr' ); ?></span>
66
+ </td>
67
+ </tr>
68
+ </table>
69
+ </div>
70
+ </div>
71
+ </script>
admin/partials/templates/tmpl-tools.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script type="text/html" id="tmpl-audit-log-result-success">
2
+ <div class="gdpr-audit-log-result">
3
+ <h2><?php echo _e( 'Result', 'gdpr' ); ?></h2>
4
+ <div class="postbox">
5
+ <div class="inside">
6
+ <textarea readonly class="gdpr-audit-log-result large-text" rows="20">{{{data.result}}}</textarea>
7
+ </div>
8
+ </div>
9
+ </div>
10
+ </script>
11
+
12
+ <script type="text/html" id="tmpl-audit-log-result-error">
13
+ <div class="gdpr-audit-log-result">
14
+ <h2><?php echo _e( 'Error', 'gdpr' ); ?></h2>
15
+ <div class="notice notice-error">
16
+ <p><?php esc_html_e( 'We could not find a any logs for that email and token combination.', 'gdpr' ); ?></p>
17
+ </div>
18
+ </div>
19
+ </script>
20
+
21
+ <script type="text/html" id="tmpl-access-data-result-success">
22
+ <div class="gdpr-access-data-result">
23
+ <h2><?php echo _e( 'Result', 'gdpr' ); ?></h2>
24
+ <p>
25
+ <form method="post" class="frm-export-data">
26
+ <?php wp_nonce_field( 'gdpr-export-data', 'gdpr_export_data_nonce' ); ?>
27
+ <input type="hidden" name="user_email" value="{{data.user_email}}">
28
+ <?php submit_button( 'XML', 'primary', 'download-data-xml', false ) ?>
29
+ <?php submit_button( 'JSON', 'primary', 'download-data-json', false ) ?>
30
+ </form>
31
+ </p>
32
+ <div class="postbox">
33
+ <div class="inside">
34
+ <div class="result">
35
+ {{{data.result}}}
36
+ </div>
37
+ </div>
38
+ </div>
39
+ </div>
40
+ </script>
41
+
42
+ <script type="text/html" id="tmpl-access-data-result-error">
43
+ <div class="gdpr-access-data-result">
44
+ <h2><?php echo _e( 'Error', 'gdpr' ); ?></h2>
45
+ <div class="notice notice-error">
46
+ <p><?php esc_html_e( 'We could not find a user with that email.', 'gdpr' ); ?></p>
47
+ </div>
48
+ </div>
49
+ </script>
admin/partials/tools.php ADDED
@@ -0,0 +1,135 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Provide a admin area view for the plugin
5
+ *
6
+ * This file is used to markup the admin-facing aspects of the plugin.
7
+ *
8
+ * @link https://trewknowledge.com
9
+ * @since 1.0.0
10
+ *
11
+ * @package GDPR
12
+ * @subpackage admin/partials
13
+ */
14
+
15
+ include_once plugin_dir_path( __FILE__ ) . 'templates/tmpl-tools.php';
16
+
17
+ if ( isset( $_GET['type'], $_GET['key'] ) ) {
18
+
19
+ if ( 'data-breach-confirmed' === $_GET['type'] ) {
20
+ $key = sanitize_text_field( wp_unslash( $_GET['key'] ) );
21
+
22
+ $data_breach = get_option( 'gdpr_data_breach_initiated', array( 'key' => '' ) );
23
+ if ( ! empty( $data_breach ) ) {
24
+ if ( $key === $data_breach['key'] ) {
25
+ GDPR_Email::prepare_data_breach_emails( $key );
26
+ delete_option( 'gdpr_data_breach_initiated' );
27
+
28
+ if ( $time = wp_next_scheduled( 'clean_gdpr_data_breach_request' ) ) {
29
+ wp_unschedule_event( $time, 'clean_gdpr_data_breach_request' );
30
+ }
31
+
32
+ add_settings_error( 'gdpr', 'resolved', esc_html__( 'Data Breach confirmed. Preparing bulk emails.', 'gdpr' ), 'updated' );
33
+ }
34
+
35
+ }
36
+
37
+
38
+ }
39
+ }
40
+
41
+ ?>
42
+
43
+ <div class="wrap gdpr">
44
+ <h1><?php esc_html_e( 'Tools', 'gdpr' ); ?></h1>
45
+ <?php settings_errors(); ?>
46
+ <div class="nav-tab-wrapper">
47
+ <?php foreach ( $tabs as $key => $value ) : ?>
48
+ <a href="<?php echo '#' . $key; ?>" class="nav-tab">
49
+ <?php echo esc_html( $value ); ?>
50
+ </a>
51
+ <?php endforeach; ?>
52
+ </div>
53
+
54
+ <div class="gdpr-tab hidden" data-id="access">
55
+ <h2><?php esc_html_e( 'Access Data', 'gdpr' ) ?></h2>
56
+ <div class="postbox not-full">
57
+ <form class="gdpr-access-data-lookup" method="post">
58
+ <div class="inside">
59
+ <?php wp_nonce_field( 'gdpr-access-data', 'gdpr_access_data_nonce' ); ?>
60
+ <h4>
61
+ <label for="gdpr-request-email-lookup"><?php esc_html_e( 'Search by email', 'gdpr' ); ?></label>
62
+ </h4>
63
+ <input type="email" name="user_email" class="regular-text" placeholder="<?php esc_attr_e( 'email@domain.com', 'gdpr' ); ?>" required>
64
+ <?php submit_button( esc_html__( 'Search', 'gdpr' ), 'primary', '', false ); ?>
65
+ <span class="spinner"></span>
66
+ </div>
67
+ </form>
68
+ </div>
69
+ </div>
70
+
71
+ <div class="gdpr-tab hidden" data-id="data-breach">
72
+ <h2><?php esc_html_e( 'Data Breach', 'gdpr' ) ?></h2>
73
+ <form class="gdpr-data-breach-form" method="post" action="<?php echo esc_url( admin_url('admin-post.php') ); ?>">
74
+ <?php wp_nonce_field( 'gdpr-data-breach', 'gdpr_data_breach_nonce' ); ?>
75
+ <input type="hidden" name="action" value="gdpr_data_breach">
76
+ <table class="form-table">
77
+ <tr>
78
+ <th><?php esc_html_e( 'Email content', 'gdpr' ) ?></th>
79
+ <td>
80
+ <textarea name="gdpr-data-breach-email-content" class="large-text" rows="5"></textarea>
81
+ <span class="description"><?php esc_html_e( 'The content that the end user will see before the below information.', 'gdpr' ) ?></span>
82
+ </td>
83
+ </tr>
84
+ <tr>
85
+ <th><?php esc_html_e( 'Nature of the personal data breach', 'gdpr' ) ?></th>
86
+ <td>
87
+ <textarea name="gdpr-data-breach-nature" class="large-text" rows="5" required></textarea>
88
+ <span class="description"><?php esc_html_e( 'Describe the nature of the personal data breach including where possible, the categories and approximate number of data subjects concerned and the categories and approximate number of personal data records concerned.', 'gdpr' ) ?></span>
89
+ </td>
90
+ </tr>
91
+ <tr>
92
+ <th><?php esc_html_e( 'Name and contact details of the data protection officer', 'gdpr' ) ?></th>
93
+ <td>
94
+ <textarea name="gdpr-name-contact-details-protection-officer" class="large-text" rows="5" required></textarea>
95
+ <span class="description"><?php esc_html_e( 'Communicate the name and contact details of the data protection officer or other contact point where more information can be obtained.', 'gdpr' ) ?></span>
96
+ </td>
97
+ </tr>
98
+ <tr>
99
+ <th><?php esc_html_e( 'Likely consequences of the personal data breach', 'gdpr' ) ?></th>
100
+ <td>
101
+ <textarea name="gdpr-likely-consequences" class="large-text" rows="5" required></textarea>
102
+ </td>
103
+ </tr>
104
+ <tr>
105
+ <th><?php esc_html_e( 'Measures taken or proposed to be taken', 'gdpr' ) ?></th>
106
+ <td>
107
+ <textarea name="gdpr-measures-taken" class="large-text" rows="5" required></textarea>
108
+ <span class="description"><?php esc_html_e( 'Describe the measures taken or proposed to be taken by the controller to address the personal data breach, including, where appropriate, measures to mitigate its possible adverse effects.', 'gdpr' ) ?></span>
109
+ </td>
110
+ </tr>
111
+ </table>
112
+ <?php submit_button( esc_html__( 'Send confirmation email', 'gdpr' ), 'primary', '', false ); ?>
113
+ </form>
114
+ </div>
115
+
116
+ <div class="gdpr-tab hidden" data-id="audit-log">
117
+ <h2><?php esc_html_e( 'Audit Log', 'gdpr' ) ?></h2>
118
+ <div class="postbox not-full">
119
+ <form class="gdpr-audit-log-lookup" method="post">
120
+ <div class="inside">
121
+ <?php wp_nonce_field( 'gdpr-audit-log', 'gdpr_audit_log_nonce' ); ?>
122
+ <h4>
123
+ <label for="gdpr-request-email-lookup"><?php esc_html_e( 'Search by email', 'gdpr' ); ?></label>
124
+ </h4>
125
+ <input type="email" name="user_email" class="regular-text" placeholder="<?php esc_attr_e( 'email@domain.com', 'gdpr' ); ?>" required>
126
+ <input type="text" name="token" placeholder="<?php esc_attr_e( '6 digit token (optional)', 'gdpr' ); ?>">
127
+ <?php submit_button( esc_html__( 'Search', 'gdpr' ), 'primary', '', false ); ?>
128
+ <span class="spinner"></span>
129
+ </div>
130
+ </form>
131
+ </div>
132
+ </div>
133
+
134
+ <!-- #poststuff -->
135
+ </div>
assets/css/gdpr-admin.css ADDED
@@ -0,0 +1 @@
 
1
+ .gdpr-settings-form #tabs,.gdpr-settings-form #consent-tabs{margin-top:20px}.gdpr-settings-form #tabs .hndle,.gdpr-settings-form #consent-tabs .hndle{font-size:16px;padding:8px 12px;margin:0;line-height:1.4}.gdpr-settings-form #tabs .hndle span,.gdpr-settings-form #consent-tabs .hndle span{font-size:12px}.privacy-page-updated-notice form{display:inline-block}.privacy-page-updated-notice form .button-secondary{vertical-align:baseline}.privacy-page-updated-notice form .button-primary:active{vertical-align:baseline}.gdpr-pending-requests-badge{display:inline-block;vertical-align:text-bottom;margin:1px 0 0 2px;padding:0 5px;min-width:7px;height:17px;border-radius:11px;background-color:#ca4a1f;color:#fff;font-size:9px;line-height:17px;text-align:center}.gdpr-request-table .spinner,.gdpr-manual-email-lookup .spinner{float:none;display:none}.gdpr-manual-email-lookup .inside{margin-bottom:0}.gdpr-request-table td{vertical-align:middle}.gdpr-request-table form{display:inline-block}.gdpr-request-table .text-center{text-align:center}.gdpr-request-table tr.review>td{padding-top:0;padding-bottom:0}.gdpr-request-table tr.review table{margin-bottom:10px}.gdpr-switch{position:relative;display:inline-block;width:45px;height:24px}.gdpr-switch input{display:none}.gdpr-switch .gdpr-slider{position:absolute;cursor:pointer;top:0;left:0;right:0;bottom:0;background-color:#ccc;-webkit-transition:.4s;-o-transition:.4s;transition:.4s}.gdpr-switch .gdpr-slider:before{position:absolute;content:"";height:16px;width:16px;left:4px;bottom:4px;background-color:white;-webkit-transition:.4s;-o-transition:.4s;transition:.4s}.gdpr-switch .gdpr-slider.round{border-radius:34px}.gdpr-switch .gdpr-slider.round:before{border-radius:50%}.gdpr-switch input:checked+.gdpr-slider{background-color:#00b9eb}.gdpr-switch input:checked+.gdpr-slider:before{-webkit-transform:translateX(21px);-ms-transform:translateX(21px);transform:translateX(21px)}.gdpr-switch input:focus+.gdpr-slider{-webkit-box-shadow:0 0 1px #00b9eb;box-shadow:0 0 1px #00b9eb}#TB_ajaxContent pre{white-space:pre-wrap;word-wrap:break-word}.gdpr-hidden{display:none}.post-type-telemetry .page-title-action{display:none}.post-type-telemetry .row-actions{display:none}.post-type-telemetry .search-box{display:none}.post-type-telemetry .actions #filter-by-date,.post-type-telemetry .actions #post-query-submit,.post-type-telemetry .actions.bulkactions{display:none}.gdpr .not-full{display:inline-block}.gdpr .spinner{display:none;visibility:visible}.gdpr .gdpr-access-data-result h2 span{font-size:16px}#tabs .postbox .inside{margin:0 !important;padding:0 20px 20px 20px}#tabs .postbox .inside .form-table{margin-top:0}#tabs .postbox .inside .tab-hosts .postbox{margin-bottom:0}#tabs .postbox .inside .tab-hosts .postbox .inside{padding-bottom:0;background-color:#f9f9f9}@media screen and (max-width: 1024px){.tab-hosts .postbox{margin-top:15px}.tab-hosts .postbox .inside{padding:10px 20px 20px 20px !important}.form-table td{padding-right:0}.form-table td .button{margin-top:5px;margin-bottom:10px}.type-telemetry *{word-wrap:break-word !important}}@media screen and (max-width: 640px){.postbox{width:100%}.postbox .notice-dismiss{padding:8px}.inside .regular-text{width:100%}.inside .button{margin-top:5px}}
assets/css/gdpr-public.css ADDED
@@ -0,0 +1 @@
 
1
+ .gdpr-noscroll{overflow:hidden;position:fixed;width:100%}.gdpr-hidden{display:none}.gdpr-overlay{position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.7);z-index:99999999;display:none}.gdpr *{font-family:Helvetica, Arial, sans-serif !important;text-transform:none !important;letter-spacing:0 !important;color:#455561;background:none;-webkit-box-shadow:none;box-shadow:none;text-shadow:none;outline:none;border:none;margin:0;padding:0}.gdpr button,.gdpr input[type="submit"]{color:#000;font-weight:normal;font-size:14px;margin:0;-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1);line-height:1.5;display:block;min-width:auto;max-width:auto}.gdpr button:before,.gdpr button:after,.gdpr input[type="submit"]:before,.gdpr input[type="submit"]:after{display:inline-block;margin:0;-webkit-transform:scale(1);-ms-transform:scale(1);transform:scale(1);top:auto;right:auto;bottom:auto;left:auto;background:none}.gdpr button:hover,.gdpr button:active,.gdpr button:focus,.gdpr input[type="submit"]:hover,.gdpr input[type="submit"]:active,.gdpr input[type="submit"]:focus{margin:0;border:none;-webkit-box-shadow:none;box-shadow:none}.gdpr img{width:100% !important}.gdpr.gdpr-privacy-bar{position:fixed;bottom:0;left:0;background:rgba(0,0,0,0.9);width:100%;color:#fff;z-index:9999999}.gdpr.gdpr-privacy-bar .gdpr-wrapper{padding:20px 40px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-sizing:border-box;box-sizing:border-box}@media screen and (max-width: 1024px){.gdpr.gdpr-privacy-bar .gdpr-wrapper{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-align:center;-ms-flex-align:center;align-items:center}}.gdpr.gdpr-privacy-bar .gdpr-wrapper:after{content:"";display:table;clear:both}.gdpr.gdpr-privacy-bar .gdpr-wrapper p{margin:0;font-size:14px;font-weight:normal}.gdpr.gdpr-privacy-bar .gdpr-wrapper .gdpr-content{-webkit-box-flex:1;-ms-flex:1;flex:1;padding-right:200px}.gdpr.gdpr-privacy-bar .gdpr-wrapper .gdpr-content p{color:#ffffff}.gdpr.gdpr-privacy-bar .gdpr-wrapper .gdpr-content a{color:#fff;text-decoration:underline}@media screen and (max-width: 1024px){.gdpr.gdpr-privacy-bar .gdpr-wrapper .gdpr-content{padding-right:0;padding-bottom:20px}}.gdpr.gdpr-privacy-bar .gdpr-wrapper .gdpr-right{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center}@media screen and (max-width: 640px){.gdpr.gdpr-privacy-bar .gdpr-wrapper .gdpr-right{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}}.gdpr.gdpr-privacy-bar .gdpr-wrapper .gdpr-right button:first-of-type{margin-left:10px}@media screen and (max-width: 640px){.gdpr.gdpr-privacy-bar .gdpr-wrapper .gdpr-right button:first-of-type{margin:0 0 10px 7px}.gdpr.gdpr-privacy-bar .gdpr-wrapper .gdpr-right button:first-of-type:before{left:-7px}}.gdpr.gdpr-privacy-bar .gdpr-wrapper .gdpr-right button:last-of-type{margin-right:0;margin-left:20px}@media screen and (max-width: 640px){.gdpr.gdpr-privacy-bar .gdpr-wrapper .gdpr-right button:last-of-type{margin:0}}.gdpr.gdpr-privacy-bar .gdpr-preferences{font-weight:normal;font-size:14px;text-decoration:underline;position:relative;margin-left:9px;color:#fff;float:left}.gdpr.gdpr-privacy-bar .gdpr-preferences:before{content:'\276F';font-size:1.1em;font-weight:normal;padding-right:5px;color:#fff;position:absolute;left:-7px;top:10px}.gdpr.gdpr-privacy-bar .gdpr-preferences:hover,.gdpr.gdpr-privacy-bar .gdpr-preferences:active,.gdpr.gdpr-privacy-bar .gdpr-preferences:focus,.gdpr.gdpr-privacy-bar .gdpr-preferences:focus-within,.gdpr.gdpr-privacy-bar .gdpr-preferences:visited{background:none}.gdpr.gdpr-privacy-bar button{margin:0 5px;padding:9px 10px}@media screen and (max-width: 640px){.gdpr.gdpr-privacy-bar button{margin:0}}.gdpr.gdpr-privacy-bar .gdpr-agreement{position:relative;font-size:13px;font-weight:normal;padding:12px 36px 12px 76px;height:auto;line-height:1.4285714;white-space:normal;margin:0;border-width:1px;border-style:solid;border-radius:3px;-webkit-box-sizing:border-box;box-sizing:border-box;background:#0085ba;border-color:#0073aa #006799 #006799;-webkit-box-shadow:0 1px 0 #006799;box-shadow:0 1px 0 #006799;color:#fff;text-decoration:none;text-shadow:0 -1px 1px #006799, 1px 0 1px #006799, 0 1px 1px #006799, -1px 0 1px #006799}.gdpr.gdpr-privacy-bar .gdpr-agreement:hover{background:#008ec2;border-color:#006799}.gdpr.gdpr-privacy-bar .gdpr-agreement:hover:before{font-size:26px;background:#fafafa;color:#00b9eb}.gdpr.gdpr-privacy-bar .gdpr-agreement:active,.gdpr.gdpr-privacy-bar .gdpr-agreement:focus{background:#0073aa;border-color:#006799;-webkit-box-shadow:inset 0 2px 0 #006799;box-shadow:inset 0 2px 0 #006799;vertical-align:top}.gdpr.gdpr-privacy-bar .gdpr-agreement:active:before,.gdpr.gdpr-privacy-bar .gdpr-agreement:focus:before{-webkit-box-shadow:inset 0 2px 0 #ccc;box-shadow:inset 0 2px 0 #ccc;vertical-align:top}.gdpr.gdpr-privacy-bar .gdpr-agreement:before{content:'\2713';top:-1px;bottom:-1px;left:-1px;position:absolute;width:42.5px;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-transition:all 0.2s;-o-transition:all 0.2s;transition:all 0.2s;-webkit-transform:translateZ(0);transform:translateZ(0);text-shadow:none;text-decoration:none;font-size:13px;line-height:26px;cursor:pointer;border-width:1px;border-style:solid;-webkit-appearance:none;-moz-appearance:none;appearance:none;border-top-left-radius:3px;border-bottom-left-radius:3px;white-space:nowrap;-webkit-box-sizing:border-box;box-sizing:border-box;color:#555;border-color:#ccc;background:#f7f7f7;-webkit-box-shadow:0 1px 0 #ccc;box-shadow:0 1px 0 #ccc;vertical-align:top;border-right:none}.gdpr.gdpr-privacy-preferences .gdpr-wrapper,.gdpr.gdpr-general-confirmation .gdpr-wrapper{position:fixed;top:50%;left:50%;-webkit-transform:translate(-50%, -50%);-ms-transform:translate(-50%, -50%);transform:translate(-50%, -50%);z-index:999999999;width:100%;max-width:768px;height:100%;max-height:500px;overflow:hidden;display:none;border-radius:2.5px;padding:15px;-webkit-box-sizing:border-box;box-sizing:border-box}.gdpr.gdpr-privacy-preferences .gdpr-wrapper form,.gdpr.gdpr-general-confirmation .gdpr-wrapper form{height:100%;position:relative}.gdpr.gdpr-privacy-preferences .gdpr-wrapper form>header,.gdpr.gdpr-general-confirmation .gdpr-wrapper form>header{display:-webkit-box;display:-ms-flexbox;display:flex;height:75px}.gdpr.gdpr-privacy-preferences .gdpr-wrapper form>header .logo,.gdpr.gdpr-general-confirmation .gdpr-wrapper form>header .logo{max-width:30%}.gdpr.gdpr-privacy-preferences .gdpr-wrapper form>header .logo a,.gdpr.gdpr-general-confirmation .gdpr-wrapper form>header .logo a{padding:0;margin:0}.gdpr.gdpr-privacy-preferences .gdpr-wrapper form>header .logo a img,.gdpr.gdpr-general-confirmation .gdpr-wrapper form>header .logo a img{display:block}.gdpr.gdpr-privacy-preferences .gdpr-wrapper form>header .gdpr-box-title,.gdpr.gdpr-general-confirmation .gdpr-wrapper form>header .gdpr-box-title{background:#23282d;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;position:relative}.gdpr.gdpr-privacy-preferences .gdpr-wrapper form>header .gdpr-box-title h3,.gdpr.gdpr-general-confirmation .gdpr-wrapper form>header .gdpr-box-title h3{margin:0 !important;padding:0 !important;text-align:center !important;color:#fff !important;font-weight:600 !important;font-size:22px !important}@media screen and (max-width: 640px){.gdpr.gdpr-privacy-preferences .gdpr-wrapper form>header .gdpr-box-title h3,.gdpr.gdpr-general-confirmation .gdpr-wrapper form>header .gdpr-box-title h3{font-size:18px !important}}.gdpr.gdpr-privacy-preferences .gdpr-wrapper form>header .gdpr-box-title .gdpr-close,.gdpr.gdpr-general-confirmation .gdpr-wrapper form>header .gdpr-box-title .gdpr-close{color:#fff;position:absolute;top:0;right:0;cursor:pointer;-webkit-transition:all 0.2s ease-in-out;-o-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out;padding:15px 15px;line-height:0}.gdpr.gdpr-privacy-preferences .gdpr-wrapper form>header .gdpr-box-title .gdpr-close:hover:before,.gdpr.gdpr-general-confirmation .gdpr-wrapper form>header .gdpr-box-title .gdpr-close:hover:before{-webkit-transform:scale(1.5);-ms-transform:scale(1.5);transform:scale(1.5)}.gdpr.gdpr-privacy-preferences .gdpr-wrapper form>header .gdpr-box-title .gdpr-close:before,.gdpr.gdpr-general-confirmation .gdpr-wrapper form>header .gdpr-box-title .gdpr-close:before{content:'\00D7';line-height:12.5px;font-size:25px;display:inline-block;-webkit-transition:all 0.2s ease-in-out;-o-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out;-webkit-transform:translateZ(0);transform:translateZ(0);-webkit-backface-visibility:hidden;backface-visibility:hidden}.gdpr.gdpr-privacy-preferences .gdpr-wrapper form>footer,.gdpr.gdpr-general-confirmation .gdpr-wrapper form>footer{position:absolute;padding:0 20px 20px 20px;bottom:0;left:160px;right:0;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:end;-ms-flex-align:end;align-items:flex-end}@media screen and (max-width: 640px){.gdpr.gdpr-privacy-preferences .gdpr-wrapper form>footer,.gdpr.gdpr-general-confirmation .gdpr-wrapper form>footer{left:0;bottom:5px}}.gdpr.gdpr-privacy-preferences .gdpr-wrapper form>footer input[type="submit"],.gdpr.gdpr-general-confirmation .gdpr-wrapper form>footer input[type="submit"]{font-size:13px;font-weight:normal;line-height:26px;height:28px;margin:0;padding:0 10px 1px;border-width:1px;border-style:solid;border-radius:3px;white-space:nowrap;-webkit-box-sizing:border-box;box-sizing:border-box;background:#0085ba;border-color:#0073aa #006799 #006799;-webkit-box-shadow:0 1px 0 #006799;box-shadow:0 1px 0 #006799;color:#fff;text-decoration:none;text-shadow:0 -1px 1px #006799, 1px 0 1px #006799, 0 1px 1px #006799, -1px 0 1px #006799}.gdpr.gdpr-privacy-preferences .gdpr-wrapper form>footer input[type="submit"]:hover,.gdpr.gdpr-general-confirmation .gdpr-wrapper form>footer input[type="submit"]:hover{background:#008ec2;border-color:#006799}.gdpr.gdpr-privacy-preferences .gdpr-wrapper form>footer input[type="submit"]:active,.gdpr.gdpr-privacy-preferences .gdpr-wrapper form>footer input[type="submit"].focus,.gdpr.gdpr-general-confirmation .gdpr-wrapper form>footer input[type="submit"]:active,.gdpr.gdpr-general-confirmation .gdpr-wrapper form>footer input[type="submit"].focus{background:#0073aa;border-color:#006799;-webkit-box-shadow:inset 0 2px 0 #006799;box-shadow:inset 0 2px 0 #006799;vertical-align:top}.gdpr.gdpr-privacy-preferences .gdpr-wrapper form>footer span,.gdpr.gdpr-general-confirmation .gdpr-wrapper form>footer span{font-size:13px;line-height:20px;color:#555d66;font-style:italic}.gdpr.gdpr-privacy-preferences .gdpr-wrapper form>footer span a,.gdpr.gdpr-general-confirmation .gdpr-wrapper form>footer span a{color:#0073aa;-webkit-transition-property:border,background,color;-o-transition-property:border,background,color;transition-property:border,background,color;-webkit-transition-duration:.05s;-o-transition-duration:.05s;transition-duration:.05s;-webkit-transition-timing-function:ease-in-out;-o-transition-timing-function:ease-in-out;transition-timing-function:ease-in-out;text-decoration:underline}.gdpr.gdpr-privacy-preferences .gdpr-wrapper form>footer span a:hover,.gdpr.gdpr-general-confirmation .gdpr-wrapper form>footer span a:hover{color:#00a0d2}.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-mobile-menu,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-mobile-menu{display:none}@media screen and (max-width: 640px){.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-mobile-menu,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-mobile-menu{display:block}}.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-mobile-menu button,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-mobile-menu button{width:100%;background-color:#191e23;color:#fff;font-size:14px;text-align:left;padding:15px;border-radius:0;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:center;-ms-flex-align:center;align-items:center;line-height:0}.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-mobile-menu button:hover,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-mobile-menu button:hover{color:#00b9eb}.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-mobile-menu button:hover:after,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-mobile-menu button:hover:after{border-top-color:#00b9eb}.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-mobile-menu button:after,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-mobile-menu button:after{content:'';width:0;height:0;border-left:7.5px solid transparent;border-right:7.5px solid transparent;border-top:7.5px solid #fff;-webkit-transition:all 0.2s;-o-transition:all 0.2s;transition:all 0.2s;right:15px;top:12px;position:absolute}.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-mobile-menu button.gdpr-active:after,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-mobile-menu button.gdpr-active:after{-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content{display:-webkit-box;display:-ms-flexbox;display:flex;height:calc( 100% - 75px);background:#f1f1f1}@media screen and (max-width: 640px){.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content{position:relative;height:calc( 100% - 119px)}}.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content .gdpr-tabs,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content .gdpr-tabs{border:none !important;min-width:160px;max-width:160px;padding:0;margin:0;overflow-y:auto;background-color:#23282d;position:relative}@media screen and (max-width: 640px){.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content .gdpr-tabs,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content .gdpr-tabs{position:absolute;height:100%;width:100%;max-width:100%;display:none;z-index:1}}.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content .gdpr-tabs li,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content .gdpr-tabs li{list-style:none}.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content .gdpr-tabs li button,.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content .gdpr-tabs li a,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content .gdpr-tabs li button,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content .gdpr-tabs li a{display:block;width:100%;background:#23282d;color:#fff;font-size:14px;text-align:left;padding:8px;border-radius:0;position:relative}@media screen and (max-width: 640px){.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content .gdpr-tabs li button,.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content .gdpr-tabs li a,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content .gdpr-tabs li button,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content .gdpr-tabs li a{padding:15px;line-height:1}}.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content .gdpr-tabs li button:hover,.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content .gdpr-tabs li a:hover,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content .gdpr-tabs li button:hover,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content .gdpr-tabs li a:hover{background-color:#191e23;color:#00b9eb}.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content .gdpr-tabs li button.gdpr-active,.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content .gdpr-tabs li a.gdpr-active,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content .gdpr-tabs li button.gdpr-active,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content .gdpr-tabs li a.gdpr-active{background-color:#0073aa}.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content .gdpr-tabs li button.gdpr-active:hover,.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content .gdpr-tabs li a.gdpr-active:hover,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content .gdpr-tabs li button.gdpr-active:hover,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content .gdpr-tabs li a.gdpr-active:hover{color:#fff}.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content .gdpr-tabs li button.gdpr-active:after,.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content .gdpr-tabs li a.gdpr-active:after,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content .gdpr-tabs li button.gdpr-active:after,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content .gdpr-tabs li a.gdpr-active:after{right:0;border:8px solid transparent;content:'';height:0;width:0;position:absolute;pointer-events:none;border-right-color:#f1f1f1;top:50%;margin-top:-8px}@media screen and (max-width: 640px){.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content .gdpr-tabs li button.gdpr-active:after,.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content .gdpr-tabs li a.gdpr-active:after,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content .gdpr-tabs li button.gdpr-active:after,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content .gdpr-tabs li a.gdpr-active:after{display:none}}.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content .gdpr-tabs li .gdpr-subtabs,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content .gdpr-tabs li .gdpr-subtabs{position:relative;padding:8px 0;top:auto;left:auto;right:auto;bottom:auto;border:0;margin:0 0 0 0;-webkit-box-shadow:none;box-shadow:none;background-color:#32373c}.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content .gdpr-tabs li .gdpr-subtabs li button,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content .gdpr-tabs li .gdpr-subtabs li button{background-color:transparent;font-size:13px;line-height:18px;padding:5px 8px}.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content .gdpr-tabs li .gdpr-subtabs li button.gdpr-active,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content .gdpr-tabs li .gdpr-subtabs li button.gdpr-active{font-weight:600}.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content .gdpr-tabs li .gdpr-subtabs li button.gdpr-active:after,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content .gdpr-tabs li .gdpr-subtabs li button.gdpr-active:after{border:none;content:''}.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content .gdpr-tab-content,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content .gdpr-tab-content{width:100%;margin-bottom:68px;overflow-y:auto}.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content .gdpr-tab-content>div,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content .gdpr-tab-content>div{display:none;padding:20px 20px 0 20px;overflow-y:auto;font-size:13px;height:100%;-webkit-box-sizing:border-box;box-sizing:border-box}.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content .gdpr-tab-content>div header,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content .gdpr-tab-content>div header{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between}.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content .gdpr-tab-content>div header h4,.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content .gdpr-tab-content>div header label,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content .gdpr-tab-content>div header h4,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content .gdpr-tab-content>div header label{margin:0}.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content .gdpr-tab-content>div header h4,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content .gdpr-tab-content>div header h4{font-weight:600 !important;padding-right:10px}.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content .gdpr-tab-content>div .gdpr-info,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content .gdpr-tab-content>div .gdpr-info{height:100%;overflow-y:auto;-webkit-box-flex:1;-ms-flex:1;flex:1;margin-top:20px;position:relative}.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content .gdpr-tab-content>div .gdpr-info>p,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content .gdpr-tab-content>div .gdpr-info>p{margin-bottom:16px}.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content .gdpr-tab-content>div .gdpr-info strong,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content .gdpr-tab-content>div .gdpr-info strong{border-bottom:1px solid rgba(0,0,0,0.4);display:block}.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content .gdpr-tab-content>div .gdpr-info .gdpr-cookies-used,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content .gdpr-tab-content>div .gdpr-info .gdpr-cookies-used{font-family:Helvetica, Arial, sans-serif;border:1px solid #e5e5e5;-webkit-box-shadow:0 1px 1px rgba(0,0,0,0.04);box-shadow:0 1px 1px rgba(0,0,0,0.04);margin-bottom:10px;background-color:#fff;font-size:13px}.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content .gdpr-tab-content>div .gdpr-info .gdpr-cookies-used:first-of-type,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content .gdpr-tab-content>div .gdpr-info .gdpr-cookies-used:first-of-type{padding-top:0}.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content .gdpr-tab-content>div .gdpr-info .gdpr-cookies-used .gdpr-cookie-title,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content .gdpr-tab-content>div .gdpr-info .gdpr-cookies-used .gdpr-cookie-title{padding:10px;border-bottom:1px solid #e1e1e1;color:#32373c;position:relative;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-pack:justify;-ms-flex-pack:justify;justify-content:space-between;-webkit-box-align:end;-ms-flex-align:end;align-items:flex-end}.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content .gdpr-tab-content>div .gdpr-info .gdpr-cookies-used .gdpr-cookie-title p,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content .gdpr-tab-content>div .gdpr-info .gdpr-cookies-used .gdpr-cookie-title p{margin:0;font-weight:600 !important}.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content .gdpr-tab-content>div .gdpr-info .gdpr-cookies-used .gdpr-cookie-title .gdpr-always-active,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content .gdpr-tab-content>div .gdpr-info .gdpr-cookies-used .gdpr-cookie-title .gdpr-always-active{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;padding:0 10px;min-height:24px;text-align:center;border-radius:50px;line-height:16px;background-color:#00b9eb;color:#fff;font-style:normal}.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content .gdpr-tab-content>div .gdpr-info .gdpr-cookies-used .gdpr-cookie-title a,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content .gdpr-tab-content>div .gdpr-info .gdpr-cookies-used .gdpr-cookie-title a{color:#0073aa}.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content .gdpr-tab-content>div .gdpr-info .gdpr-cookies-used .gdpr-cookie-title a:hover,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content .gdpr-tab-content>div .gdpr-info .gdpr-cookies-used .gdpr-cookie-title a:hover{color:#00a0d2}.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content .gdpr-tab-content>div .gdpr-info .gdpr-cookies-used .gdpr-cookies,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content .gdpr-tab-content>div .gdpr-info .gdpr-cookies-used .gdpr-cookies{color:#555;background-color:#f9f9f9;padding:10px}.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content .gdpr-tab-content>div .gdpr-info .gdpr-cookies-used .gdpr-cookies span,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content .gdpr-tab-content>div .gdpr-info .gdpr-cookies-used .gdpr-cookies span{font-style:italic}.gdpr.gdpr-privacy-preferences .gdpr-wrapper .gdpr-content .gdpr-tab-content .gdpr-active,.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content .gdpr-tab-content .gdpr-active{display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.gdpr.gdpr-general-confirmation .gdpr-wrapper{-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;max-width:400px;min-height:250px}.gdpr.gdpr-general-confirmation .gdpr-wrapper>header{display:-webkit-box;display:-ms-flexbox;display:flex;height:75px}.gdpr.gdpr-general-confirmation .gdpr-wrapper>header .logo{max-width:30%}.gdpr.gdpr-general-confirmation .gdpr-wrapper>header .logo a{padding:0;margin:0}.gdpr.gdpr-general-confirmation .gdpr-wrapper>header .logo a img{display:block}.gdpr.gdpr-general-confirmation .gdpr-wrapper>header .gdpr-box-title{background:#23282d;-webkit-box-flex:1;-ms-flex-positive:1;flex-grow:1;display:-webkit-box;display:-ms-flexbox;display:flex;-webkit-box-align:center;-ms-flex-align:center;align-items:center;-webkit-box-pack:center;-ms-flex-pack:center;justify-content:center;position:relative}.gdpr.gdpr-general-confirmation .gdpr-wrapper>header .gdpr-box-title h3{margin:0 !important;padding:0 !important;text-align:center !important;color:#fff !important;font-weight:600 !important;font-size:22px !important}@media screen and (max-width: 640px){.gdpr.gdpr-general-confirmation .gdpr-wrapper>header .gdpr-box-title h3{font-size:18px !important}}.gdpr.gdpr-general-confirmation .gdpr-wrapper>header .gdpr-box-title .gdpr-close{color:#fff;position:absolute;top:0;right:0;cursor:pointer;-webkit-transition:all 0.2s ease-in-out;-o-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out;padding:15px 15px;line-height:0}.gdpr.gdpr-general-confirmation .gdpr-wrapper>header .gdpr-box-title .gdpr-close:hover:before{-webkit-transform:scale(1.5);-ms-transform:scale(1.5);transform:scale(1.5)}.gdpr.gdpr-general-confirmation .gdpr-wrapper>header .gdpr-box-title .gdpr-close:before{content:'\00D7';line-height:12.5px;font-size:25px;display:inline-block;-webkit-transition:all 0.2s ease-in-out;-o-transition:all 0.2s ease-in-out;transition:all 0.2s ease-in-out;-webkit-transform:translateZ(0);transform:translateZ(0);-webkit-backface-visibility:hidden;backface-visibility:hidden}.gdpr.gdpr-general-confirmation .gdpr-wrapper>footer{background:#f1f1f1;padding:20px}.gdpr.gdpr-general-confirmation .gdpr-wrapper>footer button{font-size:13px;font-weight:normal;line-height:26px;height:28px;margin:0;padding:0 10px 1px;border-width:1px;border-style:solid;border-radius:3px;white-space:nowrap;-webkit-box-sizing:border-box;box-sizing:border-box;background:#0085ba;border-color:#0073aa #006799 #006799;-webkit-box-shadow:0 1px 0 #006799;box-shadow:0 1px 0 #006799;color:#fff;text-decoration:none;text-shadow:0 -1px 1px #006799, 1px 0 1px #006799, 0 1px 1px #006799, -1px 0 1px #006799;display:inline}.gdpr.gdpr-general-confirmation .gdpr-wrapper>footer button:hover{background:#008ec2;border-color:#006799}.gdpr.gdpr-general-confirmation .gdpr-wrapper>footer button:active,.gdpr.gdpr-general-confirmation .gdpr-wrapper>footer button.focus{background:#0073aa;border-color:#006799;-webkit-box-shadow:inset 0 2px 0 #006799;box-shadow:inset 0 2px 0 #006799;vertical-align:top}.gdpr.gdpr-general-confirmation .gdpr-wrapper>footer button.gdpr-cancel{color:#555;border-color:#ccc;background:#f7f7f7;-webkit-box-shadow:0 1px 0 #ccc;box-shadow:0 1px 0 #ccc;text-shadow:none}.gdpr.gdpr-general-confirmation .gdpr-wrapper>footer button.gdpr-cancel:hover{background:#fafafa;border-color:#999;color:#23282d;-webkit-box-shadow:0 1px 0 #ccc;box-shadow:0 1px 0 #ccc}.gdpr.gdpr-general-confirmation .gdpr-wrapper>footer button.gdpr-cancel:active,.gdpr.gdpr-general-confirmation .gdpr-wrapper>footer button.gdpr-cancel:focus{background:#eee;border-color:#999;-webkit-box-shadow:inset 0 2px 5px -3px rgba(0,0,0,0.5);box-shadow:inset 0 2px 5px -3px rgba(0,0,0,0.5);-webkit-transform:translateY(1px);-ms-transform:translateY(1px);transform:translateY(1px);color:#23282d}.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content{padding:20px 20px 0 20px;height:auto}.gdpr.gdpr-general-confirmation .gdpr-wrapper .gdpr-content p{margin:0}.gdpr-switch{position:relative;display:inline-block;min-width:45px;height:24px;margin-bottom:0}.gdpr-switch input{display:none}.gdpr-switch .gdpr-slider{position:absolute;cursor:pointer;top:0;left:0;right:0;bottom:0;background-color:#ccc;-webkit-transition:.4s;-o-transition:.4s;transition:.4s}.gdpr-switch .gdpr-slider:before{position:absolute;content:"";height:16px;width:16px;left:4px;bottom:4px;background-color:white;-webkit-transition:.4s;-o-transition:.4s;transition:.4s}.gdpr-switch .gdpr-slider.round{border-radius:34px}.gdpr-switch .gdpr-slider.round:before{border-radius:50%}.gdpr-switch input:checked+.gdpr-slider{background-color:#00b9eb}.gdpr-switch input:checked+.gdpr-slider:before{-webkit-transform:translateX(21px);-ms-transform:translateX(21px);transform:translateX(21px)}.gdpr-switch input:focus+.gdpr-slider{-webkit-box-shadow:0 0 1px #00b9eb;box-shadow:0 0 1px #00b9eb}.gdpr-reconsent-modal{width:100%;height:100%;position:fixed;top:0;left:0;background:#fff;z-index:9999999}.gdpr-reconsent-modal .gdpr-reconsent-modal-content{width:90%;margin:0 auto;position:absolute;top:50%;left:50%;-webkit-transform:translate(-50%, -50%);-ms-transform:translate(-50%, -50%);transform:translate(-50%, -50%)}@media screen and (min-width: 640px){.gdpr-reconsent-modal .gdpr-reconsent-modal-content{width:50%}}.gdpr-reconsent-modal .gdpr-reconsent-modal-content .gdpr-privacy-viewer{height:50vh;width:100%;border:1px solid #cecece;padding:20px;overflow:auto}.gdpr-reconsent-modal .gdpr-reconsent-modal-content textarea{resize:none}.gdpr-reconsent-modal .gdpr-reconsent-modal-content .gdpr-consent-buttons{text-align:center;margin:15px 0}.gdpr-reconsent-modal .gdpr-reconsent-modal-content .gdpr-consent-buttons a{padding:10px 20px;display:inline-block;margin:0 10px;color:#fff;border-radius:5px}.gdpr-reconsent-modal .gdpr-reconsent-modal-content .gdpr-consent-buttons a.gdpr-agree{background:#28a745}.gdpr-reconsent-modal .gdpr-reconsent-modal-content .gdpr-consent-buttons a.gdpr-disagree{color:#455561;padding:0}.gdpr-reconsent-modal .gdpr-reconsent-modal-content .gdpr-consent-loading{display:none;text-align:center;margin:15px 0}.gdpr-reconsent-modal .gdpr-reconsent-modal-content .gdpr-consent-loading .gdpr-loading{padding:10px 20px;display:inline-block;margin:0 10px}
assets/js/gdpr-admin.js ADDED
@@ -0,0 +1 @@
 
1
+ !function(t){"use strict";t(function(){function a(t){t=(t=t.replace(/^\s+|\s+$/g,"")).toLowerCase();for(var a="àáäâèéëêìíïîòóöôùúüûñç·/_,:;",e=0,n=a.length;e<n;e++)t=t.replace(new RegExp(a.charAt(e),"g"),"aaaaeeeeiiiioooouuuunc------".charAt(e));return t=t.replace(/[^a-z0-9 -]/g,"").replace(/\s+/g,"-").replace(/-+/g,"-")}t(document).on("click",".gdpr-settings-form .notice-dismiss",function(){t(this).parent().parent().remove()}),t(".add-tab").click(function(e){e.preventDefault();var n=t("#cookie-tabs");if(""!==n.val()){var i=a(n.val()),s=n.val(),d=wp.template("cookie-tabs");t("#tabs").append(d({key:i,name:s,option_name:"gdpr_cookie_popup_content"})),n.val("")}}),t(".add-consent").click(function(e){e.preventDefault();var n=t("#type-of-consent");if(""!==n.val()){var i=a(n.val()),s=n.val(),d=wp.template("consents");t("#consent-tabs").append(d({key:i,name:s,option_name:"gdpr_consent_types"})),n.val("")}}),t("#consent-tabs, #tabs").sortable(),t(document).on("click",".add-host",function(a){a.preventDefault();var e=t(this).siblings("input");if(""!==e.val()){var n=t(this).data("tabid"),i=e.val().toLowerCase().replace(" ","-"),s=wp.template("cookie-tabs-hosts");t('.tab-hosts[data-tabid="'+n+'"]').append(s({host_key:i,tab_key:n,option_name:"gdpr_cookie_popup_content"})),e.val("")}}),t(document).on("click","#tabs .notice-dismiss",function(a){a.preventDefault(),t(this).closest(".postbox").remove()}),t(document).on("click",".gdpr-request-table .gdpr-review",function(a){a.preventDefault();var e=t(this).data("index");t("tr[data-index="+e+"] div").slideToggle()}),t(document).on("click",".gdpr .nav-tab-wrapper a",function(a){var e=t(this).attr("href");if(e=e.replace("#",""),t(this).addClass("nav-tab-active"),t(this).siblings().removeClass("nav-tab-active"),t(".gdpr .gdpr-tab").addClass("hidden"),t(".gdpr .gdpr-tab[data-id="+e+"]").removeClass("hidden"),-1!==location.search.indexOf("page=gdpr-settings")){var n=t('.gdpr form input[name="_wp_http_referer"]'),i=n.val().split("#")[0];n.val(i+"#"+e)}});var e=window.location.hash;if(e){if(t('.gdpr .nav-tab-wrapper a[href="'+e+'"]').addClass("nav-tab-active"),t('.gdpr .gdpr-tab[data-id="'+e.replace("#","")+'"]').removeClass("hidden"),-1!==location.search.indexOf("page=gdpr-settings")){var n=t('.gdpr form input[name="_wp_http_referer"]'),i=n.val().split("#")[0];n.val(i+e)}}else t(".gdpr .nav-tab-wrapper a:eq(0)").addClass("nav-tab-active"),t(".gdpr .gdpr-tab:eq(0)").removeClass("hidden");t(document).on("change",".gdpr-reassign",function(){0!=t(this).val()?(t(this).closest("tr").find("td:last .button-primary").attr("disabled",!1),t(this).closest("tr").find('td:last input[name="reassign_to"]').val(t(this).val())):(t(this).closest("tr").find("td:last .button-primary").attr("disabled",!0),t(this).closest("tr").find('td:last input[name="reassign_to"]').val(""))}),t(document).on("submit",".gdpr-reassign-content",function(a){a.preventDefault();var e=t(this).find('input[name="user_email"]').val(),n=t(this).find('input[name="reassign_to"]').val(),i=t(this).find('input[name="post_type"]').val(),s=t(this).find('input[name="post_count"]').val(),d=t(this).find('input[name="gdpr_reassign_content_nonce"]').val(),o=t(this).find(".button-primary"),r=t(this).find(".spinner"),p=t(this).find("p.hidden");n&&(o.addClass("hidden"),r.addClass("is-active"),r.css("display","block"),t.post(ajaxurl,{action:"gdpr_reassign_content",user_email:e,reassign_to:n,post_type:i,post_count:s,nonce:d},function(t){r.removeClass("is-active"),r.hide(),p.removeClass("hidden"),t.success||p.text(t.data)}))}),t(document).on("submit",".gdpr-anonymize-comments",function(a){a.preventDefault();var e=t(this).find('input[name="user_email"]').val(),n=t(this).find('input[name="comment_count"]').val(),i=t(this).find('input[name="gdpr_anonymize_comments_nonce"]').val(),s=t(this).find(".button-primary"),d=t(this).find(".spinner"),o=t(this).find("p.hidden");s.addClass("hidden"),d.addClass("is-active"),d.css("display","block"),t.post(ajaxurl,{action:"gdpr_anonymize_comments",user_email:e,comment_count:n,nonce:i},function(t){d.removeClass("is-active"),d.hide(),o.removeClass("hidden"),t.success||o.text(t.data)})}),t(document).on("submit",".gdpr-access-data-lookup",function(a){a.preventDefault();var e=t(this).find('input[name="user_email"]'),n=e.val(),i=t(this).find('input[name="gdpr_access_data_nonce"]').val(),s=t(this).find(".button-primary"),d=t(this).find(".spinner"),o=t(".gdpr-access-data-result");s.addClass("hidden"),d.show(),o.remove(),e.val(""),t.post(ajaxurl,{action:"gdpr_access_data",nonce:i,email:n},function(a){if(s.removeClass("hidden"),d.hide(),a.success){var e=wp.template("access-data-result-success");t('.gdpr div[data-id="access"]').append(e({result:a.data.result,user_email:a.data.user_email}))}else{e=wp.template("access-data-result-error");t('.gdpr div[data-id="access"]').append(e())}})}),t(document).on("submit",".gdpr-audit-log-lookup",function(a){a.preventDefault();var e=t(this).find('input[name="user_email"]'),n=e.val(),i=t(this).find('input[name="token"]'),s=i.val(),d=t(this).find('input[name="gdpr_audit_log_nonce"]').val(),o=t(this).find(".button-primary"),r=t(this).find(".spinner"),p=t(".gdpr-audit-log-result");o.addClass("hidden"),r.show(),p.remove(),e.val(""),i.val(""),t.post(ajaxurl,{action:"gdpr_audit_log",nonce:d,email:n,token:s},function(a){if(o.removeClass("hidden"),r.hide(),a.success){var e=wp.template("audit-log-result-success");t('.gdpr div[data-id="audit-log"]').append(e({result:a.data}))}else{e=wp.template("audit-log-result-error");t('.gdpr div[data-id="audit-log"]').append(e())}})}),t(document).on("click",'.frm-export-data input[type="submit"]',function(a){a.preventDefault();var e=t(this).parents("form"),n=t(this).val(),i=e.find("#gdpr_export_data_nonce").val(),s=e.find('input[name="user_email"]').val(),d=n.toLowerCase();t.post(ajaxurl,{action:"gdpr_generate_data_export",nonce:i,type:n,email:s},function(a){a.success&&t("<a />",{href:"data:text/plain;charset=utf-8,"+encodeURIComponent(a.data),download:s+"."+d,text:"click"}).hide().appendTo("body")[0].click()})}),t(document).on("submit",".frm-ignore-privacy-update",function(a){a.preventDefault();var e=t(this).find('input[name="action"]').val(),n=t(this).find("#privacy-policy-ignore-update-nonce").val();t(".privacy-page-updated-notice .notice-dismiss").click(),t.post(ajaxurl,{action:e,nonce:n})})})}(jQuery);
assets/js/gdpr-public.js ADDED
@@ -0,0 +1 @@
 
1
+ !function(e){"use strict";var r=location.search,d=location.protocol+"//"+location.host+location.pathname;-1!==r.indexOf("notify=1")&&window.history.replaceState({},document.title,d),window.has_consent=function(e){if(Cookies.get("gdpr[consent_types]")&&JSON.parse(Cookies.get("gdpr[consent_types]")).indexOf(e)>-1)return!0;return!1},window.is_allowed_cookie=function(e){if(Cookies.get("gdpr[allowed_cookies]")&&JSON.parse(Cookies.get("gdpr[allowed_cookies]")).indexOf(e)>-1)return!0;return!1},e(function(){Cookies.get("gdpr[privacy_bar]")||e(".gdpr.gdpr-privacy-bar").delay(1e3).slideDown(600),!has_consent("privacy-policy")&&GDPR.is_user_logged_in&&0!=GDPR.privacy_page_id&&(e(".gdpr-reconsent-modal").show(),e("body").addClass("gdpr-noscroll"),e(".wpadminbar").hide()),e(document).on("click",".gdpr.gdpr-privacy-bar .gdpr-agreement",function(){e(".gdpr-privacy-preferences-frm").submit()}),e(document).on("submit",".gdpr-privacy-preferences-frm",function(){Cookies.set("gdpr[privacy_bar]",1,{expires:365})}),e(document).on("click",".gdpr-preferences",function(){e(this).data("type");e(".gdpr-overlay").fadeIn(),e("body").addClass("gdpr-noscroll"),e(".gdpr.gdpr-privacy-preferences .gdpr-wrapper").fadeIn()}),e(document).on("click",".gdpr.gdpr-privacy-preferences .gdpr-close, .gdpr-overlay",function(){e(".gdpr-overlay").fadeOut(),e("body").removeClass("gdpr-noscroll"),e(".gdpr.gdpr-privacy-preferences .gdpr-wrapper").fadeOut()}),e(document).on("click",".gdpr.gdpr-privacy-preferences .gdpr-tabs button",function(){var r="."+e(this).data("target");e(".gdpr.gdpr-privacy-preferences .gdpr-tab-content > div").removeClass("gdpr-active"),e(".gdpr.gdpr-privacy-preferences .gdpr-tab-content "+r).addClass("gdpr-active"),e(".gdpr.gdpr-privacy-preferences .gdpr-tabs").hasClass("gdpr-mobile-expanded")&&(e(".gdpr.gdpr-privacy-preferences .gdpr-mobile-menu button").removeClass("gdpr-active"),e(".gdpr.gdpr-privacy-preferences .gdpr-tabs").toggle()),e(".gdpr.gdpr-privacy-preferences .gdpr-tabs button").removeClass("gdpr-active"),e(".gdpr-subtabs li button").removeClass("gdpr-active"),e(this).hasClass("gdpr-tab-button")?(e(this).addClass("gdpr-active"),e(this).hasClass("gdpr-cookie-settings")&&e(".gdpr-subtabs").find("li button").first().addClass("gdpr-active")):(e(".gdpr-cookie-settings").addClass("gdpr-active"),e(this).addClass("gdpr-active"))}),e(document).on("click",".gdpr.gdpr-privacy-preferences .gdpr-mobile-menu button",function(r){e(this).toggleClass("gdpr-active"),e(".gdpr.gdpr-privacy-preferences .gdpr-tabs").toggle().addClass("gdpr-mobile-expanded")}),e(window).resize(function(){e(window).width()>640&&e(".gdpr.gdpr-privacy-preferences .gdpr-tabs").hasClass("gdpr-mobile-expanded")&&(e(".gdpr.gdpr-privacy-preferences .gdpr-mobile-menu button").removeClass("gdpr-active"),e(".gdpr.gdpr-privacy-preferences .gdpr-tabs").removeClass("gdpr-mobile-expanded").removeAttr("style"))}),e("form.gdpr-add-to-deletion-requests").on("submit",function(r){e(this).hasClass("confirmed")||(r.preventDefault(),e(".gdpr-overlay").fadeIn(),e("body").addClass("gdpr-noscroll"),e(".gdpr.gdpr-delete-confirmation .gdpr-wrapper").css({display:"flex"}).hide().fadeIn())}),e(document).on("click",".gdpr.gdpr-general-confirmation .gdpr-close, .gdpr-overlay, .gdpr-cancel",function(){e(".gdpr-overlay").fadeOut(),e(".gdpr-reconsent-modal").is(":visible")||e("body").removeClass("gdpr-noscroll"),e(".gdpr.gdpr-general-confirmation .gdpr-wrapper").fadeOut()}),e(document).on("click",".gdpr.gdpr-delete-confirmation button.gdpr-delete-account",function(){e("form.gdpr-add-to-deletion-requests").addClass("confirmed"),e('form.gdpr-add-to-deletion-requests.confirmed input[type="submit"]').click(),e(".gdpr-overlay").fadeOut(),e("body").removeClass("gdpr-noscroll"),e(".gdpr.gdpr-delete-confirmation .gdpr-wrapper").fadeOut()}),e(".gdpr-accept-confirmation").length>0&&(e(".gdpr-overlay").fadeIn(),e("body").addClass("gdpr-noscroll"),e(".gdpr.gdpr-accept-confirmation .gdpr-wrapper").css({display:"flex"}).hide().fadeIn(),e(document).on("click",".gdpr.gdpr-accept-confirmation button.gdpr-ok",function(){e(".gdpr-overlay").fadeOut(),e("body").removeClass("gdpr-noscroll"),e(".gdpr.gdpr-accept-confirmation .gdpr-wrapper").fadeOut()})),e(document).on("click",".gdpr-agree",function(r){r.preventDefault();e(this);e(".gdpr-consent-buttons").fadeOut(300,function(){e(".gdpr-consent-loading").fadeIn(300)});var d=0;setInterval(function(){e(".gdpr-ellipsis").html();d<3?(e(".gdpr-ellipsis").append("."),d++):(e(".gdpr-ellipsis").html(""),d=0)},600);e.post(GDPR.ajaxurl,{action:"agree_with_terms",nonce:e(this).data("nonce")},function(r){r.success&&(e(".gdpr-reconsent-modal").fadeOut(300,function(){e(this).remove(),e(".wpadminbar").show()}),e("body").removeClass("gdpr-noscroll"))})}),e(document).on("click",".gdpr-disagree",function(r){e(".gdpr-overlay").fadeIn(),e("body").addClass("gdpr-noscroll"),e(".gdpr.gdpr-disagree-confirmation .gdpr-wrapper").css({display:"flex"}).hide().fadeIn()}),e(document).on("click",".gdpr-disagree-confirm",function(r){r.preventDefault(),e(".gdpr-overlay").fadeOut(),e(".gdpr.gdpr-disagree-confirmation .gdpr-wrapper").fadeOut(),e(".gdpr-consent-buttons").fadeOut(300,function(){e(".gdpr-updating").html(GDPR.aborting),e(".gdpr-consent-loading").fadeIn(300)});var d=0;setInterval(function(){e(".gdpr-ellipsis").html();d<3?(e(".gdpr-ellipsis").append("."),d++):(e(".gdpr-ellipsis").html(""),d=0)},600);e.post(GDPR.ajaxurl,{action:"disagree_with_terms",nonce:e(this).data("nonce")},function(e){e.success&&location.reload()})})})}(jQuery),function(e){var r=!1;if("function"==typeof define&&define.amd&&(define(e),r=!0),"object"==typeof exports&&(module.exports=e(),r=!0),!r){var d=window.Cookies,n=window.Cookies=e();n.noConflict=function(){return window.Cookies=d,n}}}(function(){function e(){for(var e=0,r={};e<arguments.length;e++){var d=arguments[e];for(var n in d)r[n]=d[n]}return r}return function r(d){function n(r,o,t){var p;if("undefined"!=typeof document){if(arguments.length>1){if("number"==typeof(t=e({path:"/"},n.defaults,t)).expires){var a=new Date;a.setMilliseconds(a.getMilliseconds()+864e5*t.expires),t.expires=a}t.expires=t.expires?t.expires.toUTCString():"";try{p=JSON.stringify(o),/^[\{\[]/.test(p)&&(o=p)}catch(e){}o=d.write?d.write(o,r):encodeURIComponent(String(o)).replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g,decodeURIComponent),r=(r=(r=encodeURIComponent(String(r))).replace(/%(23|24|26|2B|5E|60|7C)/g,decodeURIComponent)).replace(/[\(\)]/g,escape);var i="";for(var s in t)t[s]&&(i+="; "+s,!0!==t[s]&&(i+="="+t[s]));return document.cookie=r+"="+o+i}r||(p={});for(var c=document.cookie?document.cookie.split("; "):[],g=/(%[0-9A-Z]{2})+/g,l=0;l<c.length;l++){var f=c[l].split("="),u=f.slice(1).join("=");this.json||'"'!==u.charAt(0)||(u=u.slice(1,-1));try{var m=f[0].replace(g,decodeURIComponent);if(u=d.read?d.read(u,m):d(u,m)||u.replace(g,decodeURIComponent),this.json)try{u=JSON.parse(u)}catch(e){}if(r===m){p=u;break}r||(p[m]=u)}catch(e){}}return p}}return n.set=n,n.get=function(e){return n.call(n,e)},n.getJSON=function(){return n.apply({json:!0},[].slice.call(arguments))},n.defaults={},n.remove=function(r,d){n(r,"",e(d,{expires:-1}))},n.withConverter=r,n}(function(){})});
gdpr.php ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * The plugin bootstrap file
5
+ *
6
+ * This file is read by WordPress to generate the plugin information in the plugin
7
+ * admin area. This file also includes all of the dependencies used by the plugin,
8
+ * registers the activation and deactivation functions, and defines a function
9
+ * that starts the plugin.
10
+ *
11
+ * @link https://trewknowledge.com
12
+ * @since 1.0.0
13
+ * @package GDPR
14
+ *
15
+ * @wordpress-plugin
16
+ * Plugin Name: GDPR
17
+ * Plugin URI: https://trewknowledge.com
18
+ * Description: This plugin is meant to assist a Controller, Data Processor, and Data Protection Officer (DPO) with efforts to meet the obligations and rights enacted under the GDPR.
19
+ * Version: 1.4.7
20
+ * Author: Trew Knowledge
21
+ * Author URI: https://trewknowledge.com
22
+ * License: GPL-2.0+
23
+ * License URI: http://www.gnu.org/licenses/gpl-2.0.txt
24
+ * Text Domain: gdpr
25
+ * Domain Path: /languages
26
+ */
27
+
28
+ // If this file is called directly, abort.
29
+ if ( ! defined( 'WPINC' ) ) {
30
+ die;
31
+ }
32
+
33
+ /**
34
+ * Currently plugin version.
35
+ * Start at version 1.0.0 and use SemVer - https://semver.org
36
+ * Rename this for your plugin and update it as you release new versions.
37
+ */
38
+ define( 'GDPR_VERSION', '1.4.7' );
39
+
40
+ /**
41
+ * The code that runs during plugin activation.
42
+ * This action is documented in includes/class-gdpr-activator.php
43
+ */
44
+ function activate_gdpr() {
45
+ require_once plugin_dir_path( __FILE__ ) . 'includes/class-gdpr-activator.php';
46
+ GDPR_Activator::activate();
47
+ }
48
+
49
+ /**
50
+ * The code that runs during plugin deactivation.
51
+ * This action is documented in includes/class-gdpr-deactivator.php
52
+ */
53
+ function deactivate_gdpr() {
54
+ require_once plugin_dir_path( __FILE__ ) . 'includes/class-gdpr-deactivator.php';
55
+ GDPR_Deactivator::deactivate();
56
+ }
57
+
58
+ register_activation_hook( __FILE__, 'activate_gdpr' );
59
+ register_deactivation_hook( __FILE__, 'deactivate_gdpr' );
60
+
61
+ /**
62
+ * The core plugin class that is used to define internationalization,
63
+ * admin-specific hooks, and public-facing site hooks.
64
+ */
65
+ require plugin_dir_path( __FILE__ ) . 'includes/class-gdpr.php';
66
+ require plugin_dir_path( __FILE__ ) . 'includes/helper-functions.php';
67
+
68
+
69
+ /**
70
+ * Begins execution of the plugin.
71
+ *
72
+ * Since everything within the plugin is registered via hooks,
73
+ * then kicking off the plugin from this point in the file does
74
+ * not affect the page life cycle.
75
+ *
76
+ * @since 1.0.0
77
+ */
78
+ new GDPR();
includes/class-gdpr-activator.php ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Fired during plugin activation
5
+ *
6
+ * @link https://trewknowledge.com
7
+ * @since 1.0.0
8
+ *
9
+ * @package GDPR
10
+ * @subpackage includes
11
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
12
+ */
13
+
14
+ /**
15
+ * Fired during plugin activation.
16
+ *
17
+ * This class defines all code necessary to run during the plugin's activation.
18
+ *
19
+ * @since 1.0.0
20
+ * @package GDPR
21
+ * @subpackage includes
22
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
23
+ */
24
+ class GDPR_Activator {
25
+
26
+ /**
27
+ * Runs when the user first activates the plugin.
28
+ * Sets a CRON jo to clean up the telemetry post type every 12 hours.
29
+ *
30
+ * @since 1.0.0
31
+ * @static
32
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
33
+ */
34
+ public static function activate() {
35
+ add_option( 'gdpr_disable_css', false );
36
+ add_option( 'gdpr_enable_telemetry_tracker', false );
37
+ add_option( 'gdpr_use_recaptcha', false );
38
+ add_option( 'gdpr_recaptcha_site_key', '' );
39
+ add_option( 'gdpr_recaptcha_secret_key', '' );
40
+ add_option( 'gdpr_add_consent_checkboxes_registration', true );
41
+ add_option( 'gdpr_add_consent_checkboxes_checkout', true );
42
+ }
43
+
44
+ }
includes/class-gdpr-audit-log.php ADDED
@@ -0,0 +1,170 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * The file that defines the Audit Log component
5
+ *
6
+ * A class definition that includes attributes and functions used across both the
7
+ * public-facing side of the site and the admin area.
8
+ *
9
+ * @link http://trewknowledge.com
10
+ * @since 1.0.0
11
+ *
12
+ * @package GDPR
13
+ * @subpackage includes
14
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
15
+ */
16
+
17
+ /**
18
+ * The Audit Log plugin class.
19
+ *
20
+ * This is used to help us save all interactions from the user regarding consents.
21
+ *
22
+ * @since 1.0.0
23
+ * @package GDPR
24
+ * @subpackage includes
25
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
26
+ */
27
+ class GDPR_Audit_Log {
28
+
29
+ /**
30
+ * Encrypts a string.
31
+ *
32
+ * @since 1.0.0
33
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
34
+ * @access private
35
+ * @static
36
+ * @param string $key The encryption key.
37
+ * @param string $data The data to be encrypted.
38
+ * @return string The encrypted string.
39
+ */
40
+ private static function crypt( $key, $data ) {
41
+ $iv = openssl_random_pseudo_bytes( openssl_cipher_iv_length( 'aes-256-cbc' ) );
42
+ $encrypted = openssl_encrypt( $data, 'aes-256-cbc', $key, 0, $iv );
43
+ return base64_encode( $encrypted . '::' . $iv );
44
+ }
45
+
46
+ /**
47
+ * Decrypts a string.
48
+ * @since 1.0.0
49
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
50
+ * @access private
51
+ * @static
52
+ * @param string $key The encryption key.
53
+ * @param string $data The data to be decrypted.
54
+ * @return string The decrypted string.
55
+ */
56
+ private static function decrypt( $key, $data ) {
57
+ list( $encrypted_data, $iv ) = explode( '::', base64_decode( $data ), 2 );
58
+ return openssl_decrypt( $encrypted_data, 'aes-256-cbc', $key, 0, $iv );
59
+ }
60
+
61
+ /**
62
+ * Logs something to our audit log.
63
+ * @since 1.0.0
64
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
65
+ * @static
66
+ * @param int $user_id The user ID.
67
+ * @param string $input The string to be logged.
68
+ */
69
+ public static function log( $user_id, $input ) {
70
+ $user = get_user_by( 'ID', $user_id );
71
+ $date = '[' . date('Y/m/d H:i:s') . '] ';
72
+ $encrypted = self::crypt( $user->user_email, $date . $input);
73
+ add_user_meta( $user_id, 'gdpr_audit_log', $encrypted );
74
+ }
75
+
76
+ /**
77
+ * Returns the existing logs for an email.
78
+ * @since 1.0.0
79
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
80
+ * @static
81
+ * @param string $email The data subject email.
82
+ * @param string $token A 6 digit token that is provided on user deletion.
83
+ * @return string The decrypted log.
84
+ */
85
+ public static function get_log( $email, $token = null ) {
86
+ // Try getting an existing user
87
+ $user = get_user_by( 'email', $email );
88
+ if ( $user instanceof WP_User ) {
89
+ $user_log = get_user_meta( $user->ID, 'gdpr_audit_log', false );
90
+ ob_start();
91
+ foreach ( $user_log as $log ) {
92
+ echo self::decrypt( $email, $log ) . "\n";
93
+ }
94
+ $log = ob_get_clean();
95
+ } else {
96
+ $uploads_dir = wp_upload_dir();
97
+ $basedir = $uploads_dir['basedir'];
98
+ $path = $basedir . '/gdpr_logs/';
99
+ $email_masked = self::email_mask( $email . $token );
100
+ $filename = base64_encode( $email_masked );
101
+ $file_found = file_exists( $path . $filename );
102
+ if ( ! $file_found ) {
103
+ return false;
104
+ } else {
105
+ $log = file_get_contents( $path . $filename );
106
+ return self::decrypt( $email, $log );
107
+ }
108
+ }
109
+
110
+ return $log;
111
+ }
112
+
113
+ /**
114
+ * Mask the email so it's not identifiable.
115
+ * @since 1.0.0
116
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
117
+ * @access private
118
+ * @static
119
+ * @param string $email The email to mask.
120
+ * @param string $character The character that will replace letters.
121
+ * @return string The masked email.
122
+ */
123
+ private static function email_mask( $email, $character = '-' ){
124
+ $email_arr = explode( '@', $email, 2 );
125
+
126
+ $length = strlen( $email_arr[0] );
127
+ $suplement = ( 0 !== $length % 2) ? 1 : 0;
128
+ $length = floor( $length / 2 );
129
+ $username = substr( $email_arr[0], 0, $length ) . str_repeat( $character, $length + $suplement );
130
+
131
+ $length = strlen( $email_arr[1] );
132
+ $suplement = ( 0 !== $length % 2) ? 1 : 0;
133
+ $length = floor( $length / 2 );
134
+ $domain = str_repeat( $character, $length + $suplement ) . substr( $email_arr[1], -$length, $length );
135
+
136
+ return $username . '@' . $domain;
137
+ }
138
+
139
+ /**
140
+ * Exports the user audit log to a file.
141
+ * @since 1.0.0
142
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
143
+ * @static
144
+ * @param int $user_id The user ID
145
+ * @param string $token The 6 digit token the user gets on deletion.
146
+ */
147
+ public static function export_log( $user_id, $token ) {
148
+ $user = get_user_by( 'ID', $user_id );
149
+ if ( ! $user instanceof WP_User ) {
150
+ return;
151
+ }
152
+
153
+ $uploads_dir = wp_upload_dir();
154
+ $basedir = $uploads_dir['basedir'];
155
+ $path = $basedir . '/gdpr_logs/';
156
+
157
+ if ( wp_mkdir_p( $path ) ) {
158
+ if ( ! file_exists( $path . 'index.php' ) ) {
159
+ file_put_contents( $path . 'index.php', '' );
160
+ }
161
+ $log = self::get_log( $user->user_email );
162
+ $filename = self::email_mask( $user->user_email . $token );
163
+ $filename = base64_encode( $filename );
164
+
165
+ file_put_contents( $path . $filename, self::crypt( $user->user_email, $log ) );
166
+ }
167
+
168
+ }
169
+
170
+ }
includes/class-gdpr-deactivator.php ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Fired during plugin deactivation
5
+ *
6
+ * @link https://trewknowledge.com
7
+ * @since 1.0.0
8
+ *
9
+ * @package GDPR
10
+ * @subpackage includes
11
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
12
+ */
13
+
14
+ /**
15
+ * Fired during plugin deactivation.
16
+ *
17
+ * This class defines all code necessary to run during the plugin's deactivation.
18
+ *
19
+ * @since 1.0.0
20
+ * @package GDPR
21
+ * @subpackage includes
22
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
23
+ */
24
+ class GDPR_Deactivator {
25
+
26
+ /**
27
+ * Runs when the user deactivates the plugin.
28
+ * Clears the CRON job that deletes telemetry posts every 12 hours.
29
+ *
30
+ * @since 1.0.0
31
+ * @static
32
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
33
+ */
34
+ public static function deactivate() {
35
+ wp_clear_scheduled_hook('telemetry_cleanup');
36
+ }
37
+
38
+ }
includes/class-gdpr-email.php ADDED
@@ -0,0 +1,229 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This file handle emailing users.
4
+ *
5
+ * @link http://trewknowledge.com
6
+ * @since 1.0.0
7
+ *
8
+ * @package GDPR
9
+ * @subpackage includes
10
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
11
+ */
12
+
13
+ /**
14
+ * Handles emailing users.
15
+ *
16
+ * @since 1.0.0
17
+ * @package GDPR
18
+ * @subpackage includes
19
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
20
+ */
21
+ class GDPR_Email {
22
+ /**
23
+ * Locate template.
24
+ *
25
+ * Locate the called template.
26
+ * Search Order:
27
+ * 1. /themes/theme/gdpr/templates/email/$template_name
28
+ * 2. /plugins/gdpr/templates/$template_name.
29
+ *
30
+ * @since 1.0.0
31
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
32
+ * @access private
33
+ * @static
34
+ * @param string $template_name Template to load.
35
+ * @return string Path to the template file.
36
+ */
37
+ private static function locate_template( $template_name ) {
38
+ // Set variable to search in gdpr folder of theme.
39
+ $theme_path = 'gdpr/email/';
40
+
41
+ // Set default plugin templates path.
42
+ $plugin_path = plugin_dir_path( dirname( __FILE__ ) ) . 'templates/email/'; // Path to the template folder
43
+
44
+ // Search template file in theme folder.
45
+ $template = locate_template( array(
46
+ $theme_path . $template_name
47
+ ) );
48
+
49
+ // Get plugins template file.
50
+ if ( ! $template ) {
51
+ $template = $plugin_path . $template_name;
52
+ }
53
+ return $template;
54
+ }
55
+
56
+ /**
57
+ * Get template.
58
+ *
59
+ * Search for the template and include the file.
60
+ *
61
+ * @since 1.0.0
62
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
63
+ * @access private
64
+ * @static
65
+ * @param string $template_name Template to load.
66
+ * @param array $args Arguments passed to the template file.
67
+ */
68
+ private static function get_template( $template_name, $args = array() ) {
69
+ $template_file = self::locate_template( $template_name );
70
+
71
+ if ( ! file_exists( $template_file ) ) {
72
+ return;
73
+ }
74
+ include $template_file;
75
+ }
76
+
77
+ /**
78
+ * Get the email content from the correct file.
79
+ * @since 1.0.0
80
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
81
+ * @static
82
+ * @param string $template_name Template to load.
83
+ * @param array $args Arguments passed to the template file.
84
+ * @return string Email contents.
85
+ */
86
+ public static function get_email_content( $template_name, $args = array() ) {
87
+ ob_start();
88
+ self::get_template( $template_name, $args );
89
+ return ob_get_clean();
90
+ }
91
+
92
+ /**
93
+ * Get a noreply email address.
94
+ * @since 1.0.0
95
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
96
+ * @access private
97
+ * @static
98
+ * @return string The noreply email address
99
+ */
100
+ private static function get_do_not_reply_address() {
101
+ $sitename = strtolower( $_SERVER['SERVER_NAME'] );
102
+ if ( substr( $sitename, 0, 4 ) === 'www.' ) {
103
+ $sitename = substr( $sitename, 4 );
104
+ }
105
+
106
+ return apply_filters( 'gdpr_do_not_reply_address', 'noreply@' . $sitename );
107
+ }
108
+
109
+ /**
110
+ * Create batches of users so we can throtle emails.
111
+ * Schedule CRON jobs every hour that sends the current batch of emails.
112
+ * @since 1.0.0
113
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
114
+ * @static
115
+ * @param string $key The confirmation key.
116
+ */
117
+ public static function prepare_data_breach_emails( $key ) {
118
+ $data_breach = get_option( 'gdpr_data_breach_initiated', array( 'key' => '' ) );
119
+ if ( $key !== $data_breach['key'] ) {
120
+ return;
121
+ }
122
+
123
+ $limit = get_option( 'gdpr_email_limit', 100 );
124
+
125
+ $users = get_users( array(
126
+ 'fields' => 'all_with_meta'
127
+ ) );
128
+
129
+ $steps = ceil( count( $users ) / $limit );
130
+
131
+ foreach ( range( 0, $steps - 1 ) as $loop ) {
132
+ $offset = $limit * $loop;
133
+ $loop_emails = wp_list_pluck( $users, 'user_email' );
134
+ $loop_emails = array_slice( $loop_emails, $offset, $limit );
135
+ wp_schedule_single_event( time() + $loop * HOUR_IN_SECONDS, 'send_data_breach_emails', array( $loop_emails, $data_breach ) );
136
+ }
137
+ }
138
+
139
+ /**
140
+ * The CRON job set by the prepare_data_breach_emails calls this function.
141
+ * This sends one of the data breach batch emails.
142
+ * @since 1.0.0
143
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
144
+ * @param array $emails The batch recipients.
145
+ * @param string $data The contents of the email.
146
+ */
147
+ public function send_data_breach_emails( $emails, $data ) {
148
+ $content = isset( $data['content'] ) ? sanitize_textarea_field( $data['content'] ) : '';
149
+
150
+ $nature = sanitize_textarea_field( wp_unslash( $data['nature'] ) );
151
+ $office_contact = sanitize_textarea_field( wp_unslash( $data['office_contact'] ) );
152
+ $consequences = sanitize_textarea_field( wp_unslash( $data['consequences'] ) );
153
+ $measures = sanitize_textarea_field( wp_unslash( $data['measures'] ) );
154
+
155
+ foreach ( (array) $emails as $email ) {
156
+ $user = get_user_by( 'email', $email );
157
+ if ( $user instanceof WP_User ) {
158
+ GDPR_Audit_Log::log( $user->ID, esc_html__( 'Data breach notification sent to user.', 'gdpr' ) );
159
+ /* translators: email content */
160
+ GDPR_Audit_Log::log( $user->ID, sprintf( esc_html__( 'Email content: %s', 'gdpr'), $content ) );
161
+ /* translators: nature of the data breach */
162
+ GDPR_Audit_Log::log( $user->ID, sprintf( esc_html__( 'Nature of data breach: %s', 'gdpr'), $nature ) );
163
+ /* translators: data protection officer contact information */
164
+ GDPR_Audit_Log::log( $user->ID, sprintf( esc_html__( 'Data protection officer contact: %s', 'gdpr'), $office_contact ) );
165
+ /* translators: likely consequences */
166
+ GDPR_Audit_Log::log( $user->ID, sprintf( esc_html__( 'Likely consequences of breach: %s', 'gdpr'), $consequences ) );
167
+ /* translators: measures taken */
168
+ GDPR_Audit_Log::log( $user->ID, sprintf( esc_html__( 'Measures taken or proposed to be taken: %s', 'gdpr'), $measures ) );
169
+ }
170
+ }
171
+
172
+
173
+ self::send( $emails, 'data-breach-notification', array(
174
+ 'content' => $content,
175
+ 'nature' => $nature,
176
+ 'office_contact' => $office_contact,
177
+ 'consequences' => $consequences,
178
+ 'measures' => $measures,
179
+ ) );
180
+ }
181
+
182
+ /**
183
+ * Actually send an email.
184
+ * This check if the type is one of the possible types of email.
185
+ * Set the headers. Get the email content from the correct file.
186
+ * @since 1.0.0
187
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
188
+ * @static
189
+ * @param array $emails The recipient email addresses.
190
+ * @param string $type The email type.
191
+ * @param array $args The arguments to be used by the email template.
192
+ * @param array $attachments Attachments
193
+ * @return bool Whether the email contents were sent successfully.
194
+ */
195
+ public static function send( $emails, $type, $args = array(), $attachments = array() ) {
196
+ $possible_types = apply_filters( 'gdpr_email_types', array(
197
+ 'new-request' => apply_filters( 'gdpr_new_request_email_subject', esc_html__( 'GDPR Notification: There is a new request waiting to be reviewed.', 'gdpr' ) ),
198
+ 'delete-request' => apply_filters( 'gdpr_delete_request_email_subject', esc_html__( 'Someone requested to close your account.', 'gdpr' ) ),
199
+ 'delete-resolved' => apply_filters( 'gdpr_delete_resolved_email_subject', esc_html__( 'Your account has been closed.', 'gdpr' ) ),
200
+ 'rectify-request' => apply_filters( 'gdpr_rectify_request_email_subject', esc_html__( 'Someone requested that we rectify data of your account.', 'gdpr' ) ),
201
+ 'rectify-resolved' => apply_filters( 'gdpr_rectify_resolved_email_subject', esc_html__( 'Your request has been completed.', 'gdpr' ) ),
202
+ 'complaint-request' => apply_filters( 'gdpr_complaint_request_email_subject', esc_html__( 'Someone made complaint on behalf of your account.', 'gdpr' ) ),
203
+ 'complaint-resolved' => apply_filters( 'gdpr_complaint_resolved_email_subject', esc_html__( 'Your request has been completed.', 'gdpr' ) ),
204
+ 'export-data-request' => apply_filters( 'gdpr_export_data_request_email_subject', esc_html__( 'Someone requested to download your data.', 'gdpr' ) ),
205
+ 'export-data-resolved' => apply_filters( 'gdpr_export_data_resolved_email_subject', esc_html__( 'Your request has been completed.', 'gdpr' ) ),
206
+ 'data-breach-request' => apply_filters( 'gdpr_data_breach_request_email_subject', esc_html__( 'Someone requested to send a data breach notification.', 'gdpr' ) ),
207
+ 'data-breach-notification' => apply_filters( 'gdpr_data_breach_resolved_email_subject', esc_html__( 'Data Breach Notification.', 'gdpr' ) ),
208
+ ) );
209
+
210
+ if ( ! in_array( $type, array_keys( $possible_types ), true ) ) {
211
+ return;
212
+ }
213
+
214
+ $no_reply = self::get_do_not_reply_address();
215
+ $headers = array( 'From: ' . get_bloginfo( 'name' ) . ' <' . $no_reply . '>' );
216
+ foreach ( (array) $emails as $email ) {
217
+ $headers[] = 'Bcc: ' . sanitize_email( $email );
218
+ }
219
+
220
+ $content = self::get_email_content( $type . '.php', $args );
221
+
222
+ return wp_mail( $no_reply,
223
+ $possible_types[ $type ],
224
+ html_entity_decode( $content, ENT_QUOTES, 'UTF-8' ),
225
+ $headers,
226
+ ( ! empty( $attachments ) ) ? $attachments : array()
227
+ );
228
+ }
229
+ }
includes/class-gdpr-help.php ADDED
@@ -0,0 +1,188 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * This file is responsible for adding help sections to the plugin pages.
4
+ *
5
+ * @link http://trewknowledge.com
6
+ * @since 0.1.0
7
+ *
8
+ * @package GDPR
9
+ * @subpackage includes
10
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
11
+ */
12
+
13
+ /**
14
+ * A class that adds help tabs to the plugin pages.
15
+ *
16
+ * @since 1.0.0
17
+ * @package GDPR
18
+ * @subpackage includes
19
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
20
+ */
21
+ class GDPR_Help {
22
+
23
+ /**
24
+ * Add the requests page help tabs.
25
+ * @since 1.0.0
26
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
27
+ * @static
28
+ */
29
+ public static function add_requests_help() {
30
+ $overview = '<h2>' . esc_html__( 'Overview', 'gdpr' ) . '</h2>' .
31
+ '<p>' . esc_html__( 'This page has multiple request tables. Users can request multiple things like getting deleted from the site or having their data rectified. All requests will come to these tables.', 'gdpr' ) . '</p>';
32
+ get_current_screen()->add_help_tab( array(
33
+ 'id' => 'overview',
34
+ 'title' => esc_html__( 'Overview', 'gdpr' ),
35
+ 'content' => $overview,
36
+ ) );
37
+
38
+ $rectify_help = '<h2>' . esc_html__( 'Rectify Data', 'gdpr' ) . '</h2>' .
39
+ '<p>' . esc_html__( 'Users may request to have their data rectified. They can place a request somewhere on your site and those requests will show up here.', 'gdpr' ) . '</p>' .
40
+ '<p>' . esc_html__( 'When you complete the request, mark it as resolved and the requester will get a notification email confirming that their request was resolved.', 'gdpr' ) . '</p>';
41
+ get_current_screen()->add_help_tab( array(
42
+ 'id' => 'rectify-data',
43
+ 'title' => esc_html__( 'Rectify Data', 'gdpr' ),
44
+ 'content' => $rectify_help,
45
+ ) );
46
+
47
+ $complaint_help = '<h2>' . esc_html__( 'Complaints', 'gdpr' ) . '</h2>' .
48
+ '<p>' . esc_html__( 'Users may complain about something that happened. They can place a complaint somewhere on your site and those complaints will show up here.', 'gdpr' ) . '</p>' .
49
+ '<p>' . esc_html__( 'When you resolve the problem, mark it as resolved and the requester will get a notification email confirming that his complaint was resolved.', 'gdpr' ) . '</p>';
50
+ get_current_screen()->add_help_tab( array(
51
+ 'id' => 'complaint',
52
+ 'title' => esc_html__( 'Complaints', 'gdpr' ),
53
+ 'content' => $complaint_help,
54
+ ) );
55
+
56
+ $erasure_help = '<h2>' . esc_html__( 'Erasure', 'gdpr' ) . '</h2>' .
57
+ '<p>' . esc_html__( 'Users may request to be deleted from the site. If they don\'t have any content published on the site (including comments) they will be removed from the site automatically. Otherwise, they will show up at this review table where you can reassign or delete their published content and anonymize his comments.', 'gdpr' ) . '</p>' .
58
+ '<p>' . esc_html__( 'When you are ready to delete the user, they will get a notification that their account has been closed. According to GDPR, you have 30 days to fulfill this request. On some occasions, you can ask to extend this time.', 'gdpr' ) . '</p>';
59
+ get_current_screen()->add_help_tab( array(
60
+ 'id' => 'erasure',
61
+ 'title' => esc_html__( 'Erasures', 'gdpr' ),
62
+ 'content' => $erasure_help,
63
+ ) );
64
+ }
65
+
66
+ /**
67
+ * Add the tools page help tabs.
68
+ * @since 1.0.0
69
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
70
+ * @static
71
+ */
72
+ public static function add_tools_help() {
73
+ $overview = '<h2>' . esc_html__( 'Overview', 'gdpr' ) . '</h2>' .
74
+ '<p>' . esc_html__( 'We added tools to make your life easier when you need to perform administrative tasks like notify all your users of a possible data breach.', 'gdpr' ) . '</p>';
75
+ get_current_screen()->add_help_tab( array(
76
+ 'id' => 'overview',
77
+ 'title' => esc_html__( 'Overview', 'gdpr' ),
78
+ 'content' => $overview,
79
+ ) );
80
+
81
+ $access_data_help = '<h2>' . esc_html__( 'Access Data', 'gdpr' ) . '</h2>' .
82
+ '<p>' . esc_html__( 'Use this page to look for all known data about a user. You can look it up using the user\'s email address and are able to download it in XML and JSON formats.', 'gdpr' ) . '</p>';
83
+ get_current_screen()->add_help_tab( array(
84
+ 'id' => 'access-data',
85
+ 'title' => esc_html__( 'Access Data', 'gdpr' ),
86
+ 'content' => $access_data_help,
87
+ ) );
88
+
89
+ $data_breach_help = '<h2>' . esc_html__( 'Data Breach Notification', 'gdpr' ) . '</h2>' .
90
+ '<p><strong>' . esc_html__( 'Use this carefully.', 'gdpr' ) . '</strong></p>' .
91
+ '<p>' . esc_html__( 'This will send a mass email to all your users with the information provided on these fields. This email is throttled based on the hourly limit set on the plugin settings page. ', 'gdpr' ) . '</p>' .
92
+ '<p><strong>' . esc_html__( 'Only use this tool if you believe your site has been compromised and that your user\'s personal data might have been leaked.', 'gdpr' ) . '</strong></p>';
93
+ get_current_screen()->add_help_tab( array(
94
+ 'id' => 'data-breach',
95
+ 'title' => esc_html__( 'Data Breach', 'gdpr' ),
96
+ 'content' => $data_breach_help,
97
+ ) );
98
+
99
+ $audit_log_help = '<h2>' . esc_html__( 'Audit Log', 'gdpr' ) . '</h2>' .
100
+ '<p><strong>' . esc_html__( 'We do not log any of the user\'s personal data.', 'gdpr' ) . '</strong></p>' .
101
+ '<p>' . esc_html__( 'All logs are encrypted before saving to the database. An encrypted log file is created whenever a user gets removed from the site.', 'gdpr' ) . '</p>' .
102
+ '<p>' . esc_html__( 'This tool will keep a record of some actions such as changing consent preferences, placing a request, data breach notifications received, etc…', 'gdpr' ) . '<br />' .
103
+ esc_html__( 'The only way to read the logs is to search for the user email. If the data subject is not a registered site user anymore, you need to ask for the 6 digit token that was provided during deletion. That will allow this tool to look for a log file with his information.', 'gdpr' ) . '</p>';
104
+ get_current_screen()->add_help_tab( array(
105
+ 'id' => 'audit-log',
106
+ 'title' => esc_html__( 'Audit Log', 'gdpr' ),
107
+ 'content' => $audit_log_help,
108
+ ) );
109
+ }
110
+
111
+ /**
112
+ * Add the settings page help tabs.
113
+ * @since 1.0.0
114
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
115
+ * @static
116
+ */
117
+ public static function add_settings_help() {
118
+ $general_settings_help = '<h2>' . esc_html__( 'General Settings', 'gdpr' ) . '</h2>' .
119
+ '<p>' . esc_html__( 'This plugin needs to know your privacy policy page to track updates to it and ask users to re-consent to your new terms.', 'gdpr' ) . '</p>' .
120
+ '<p>' . esc_html__( 'When sending a data breach notification to your users, we need to throttle the emails because of server limitations. This is an hourly limit. Check with your hosting provider before changing this value.', 'gdpr' ) . '</p>';
121
+ get_current_screen()->add_help_tab( array(
122
+ 'id' => 'general',
123
+ 'title' => esc_html__( 'General Settings', 'gdpr' ),
124
+ 'content' => $general_settings_help,
125
+ ) );
126
+
127
+ $cookies_settings_help = sprintf( '<h2>' . esc_html__( 'Cookie Management', 'gdpr' ) . '</h2>' .
128
+ '<p>' . esc_html__( 'Fill out every information you can about the cookies your site uses. Set the cookies that you set under Cookies Used and cookies used and set by third parties under the hosts section.', 'gdpr' ) . '</p>' .
129
+ /* translators: the function */
130
+ '<p>' . esc_html__( 'You must ask your developer to wrap the code that sets the cookies with our helper function %s.', 'gdpr' ) . '</p>' .
131
+ '<p>' . esc_html__( 'Some services like Google Analytics provide a way to opt out from their code with an extra parameter to their snippet.', 'gdpr' ) . '</p>' .
132
+ '<h3>' . esc_html__( 'External Links', 'gdpr' ) . '</h3>' .
133
+ '<ul>' .
134
+ '<li><a href="https://codex.wordpress.org/WordPress_Cookies" title="' . esc_attr__( 'WordPress cookies', 'gdpr' ) . '" target="_blank">'. esc_html__( 'WordPress cookies', 'gdpr' ) .'</a></li>' .
135
+ '</ul>',
136
+ '<code>is_allowed_cookie( $cookie_name )</code>'
137
+ );
138
+ get_current_screen()->add_help_tab( array(
139
+ 'id' => 'cookies',
140
+ 'title' => esc_html__( 'Cookie Management', 'gdpr' ),
141
+ 'content' => $cookies_settings_help,
142
+ ) );
143
+
144
+ $consent_settings_help = sprintf( '<h2>' . esc_html__( 'Consent Management ( Coming Soon )', 'gdpr' ) . '</h2>' .
145
+ '<p>' . esc_html__( 'All consents are disabled by default. On first registration, your users will need to consent to your privacy policy. Depending on your privacy policy you should register multiple types of consent on this page and allow them to be toggled on/off.', 'gdpr' ) . '</p>' .
146
+ /* translators: the function */
147
+ '<p>' . esc_html__( 'If you have an optional consent type, you must have a developer wrap the functionality in our helper function %s.', 'gdpr' ) . '</p>' .
148
+ '<p><strong>' . esc_html__( 'i.e.', 'gdpr' ) . '</strong><br />' . esc_html__( 'You registered email marketing as an optional consent but the user did not actively opt into it on their profile page. You should have your email capture form wrapped in our helper function to block registration or better yet, not even display the email capture form. Same goes for blocking adding the user to your mailing system on registration if consent is not given.', 'gdpr' ) . '</p>' .
149
+ '<h3>' . esc_html__( 'External Links', 'gdpr' ) . '</h3>' .
150
+ '<ul>' .
151
+ '<li><a href="https://gdpr-info.eu/art-7-gdpr/" title="' . esc_attr__( 'Article 7 - Conditions for consent', 'gdpr' ) . '" target="_blank">'. esc_html__( 'Article 7 - Conditions for consent', 'gdpr' ) .'</a></li>' .
152
+ '<li><a href="https://gdpr-info.eu/art-8-gdpr/" title="' . esc_attr__( "Article 8 - conditions applicable to child's consent in relation to information society services", 'gdpr' ) . '" target="_blank">'. esc_html__( "Article 8 - conditions applicable to child's consent in relation to information society services", 'gdpr' ) .'</a></li>' .
153
+ '<li><a href="https://gdpr-info.eu/recitals/no-42/" title="' . esc_attr__( 'Recital 42 - Burden of proof and requirements for consent', 'gdpr' ) . '" target="_blank">'. esc_html__( 'Recital 42 - Burden of proof and requirements for consent', 'gdpr' ) .'</a></li>' .
154
+ '<li><a href="https://gdpr-info.eu/recitals/no-43/" title="' . esc_attr__( 'Recital 43 - Freely Given consent', 'gdpr' ) . '" target="_blank">'. esc_html__( 'Recital 43 - Freely Given consent', 'gdpr' ) .'</a></li>' .
155
+ '</ul>',
156
+ '<code>have_consent( $consent_id )</code>'
157
+ );
158
+
159
+ get_current_screen()->add_help_tab( array(
160
+ 'id' => 'consents',
161
+ 'title' => esc_html__( 'Consent Management', 'gdpr' ),
162
+ 'content' => $consent_settings_help,
163
+ ) );
164
+ }
165
+
166
+ /**
167
+ * Add the telemetry page help tabs.
168
+ * @since 1.0.0
169
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
170
+ * @static
171
+ */
172
+ public static function add_telemetry_help() {
173
+ if ( 'edit-telemetry' !== get_current_screen()->id ) {
174
+ return;
175
+ }
176
+
177
+ $telemetry_help = '<h2>' . esc_html__( 'Overview', 'gdpr' ) . '</h2>' .
178
+ '<p>' . esc_html__( 'This is all data that are being sent outside of your site. WordPress send some data to it\'s servers to be able to do automatic updates. You can reduce the amount of data being sent using filters.', 'gdpr' ) . '</p>' .
179
+ '<p>' . esc_html__( 'Some plugins also capture data and send it to their servers. Such practice is not allowed for plugins hosted on wordpress.org plugin repository. In case this is a Premium plugin, you should have been given the option to choose which type of data you want to send.', 'gdpr' ) . '</p>' .
180
+ '<p>' . esc_html__( 'Use this tool to identify plugins or themes sending potential personal data outside of your server and take action if necessary.', 'gdpr' ) . '</p>' .
181
+ '<p>' . esc_html__( 'All information on this page is automatically deleted every 12 hours so this doesn\'t grow too large and slow your site.' ) . '</p>';
182
+ get_current_screen()->add_help_tab( array(
183
+ 'id' => 'overview',
184
+ 'title' => esc_html__( 'Overview', 'gdpr' ),
185
+ 'content' => $telemetry_help,
186
+ ) );
187
+ }
188
+ }
includes/class-gdpr-requests.php ADDED
@@ -0,0 +1,265 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * The requests functionality of the plugin.
5
+ *
6
+ * @link https://trewknowledge.com
7
+ * @since 1.0.0
8
+ *
9
+ * @package GDPR
10
+ * @subpackage includes
11
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
12
+ */
13
+
14
+ /**
15
+ * The requests functionality of the plugin.
16
+ *
17
+ * @package GDPR
18
+ * @subpackage includes
19
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
20
+ */
21
+ class GDPR_Requests {
22
+
23
+ /**
24
+ * The ID of this plugin.
25
+ *
26
+ * @since 1.0.0
27
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
28
+ * @access protected
29
+ * @static
30
+ * @var string $plugin_name The ID of this plugin.
31
+ */
32
+ protected static $plugin_name;
33
+
34
+ /**
35
+ * The version of this plugin.
36
+ *
37
+ * @since 1.0.0
38
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
39
+ * @access protected
40
+ * @static
41
+ * @var string $version The current version of this plugin.
42
+ */
43
+ protected static $version;
44
+
45
+ /**
46
+ * Allowed types of requests.
47
+ *
48
+ * @since 1.0.0
49
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
50
+ * @access protected
51
+ * @static
52
+ * @var array
53
+ */
54
+ protected static $allowed_types = array( 'export-data', 'rectify', 'complaint', 'delete' );
55
+
56
+
57
+ /**
58
+ * Initialize the class and set its properties.
59
+ *
60
+ * @since 1.0.0
61
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
62
+ * @param string $plugin_name The name of this plugin.
63
+ * @param string $version The version of this plugin.
64
+ */
65
+ public function __construct( $plugin_name, $version ) {
66
+ self::$plugin_name = $plugin_name;
67
+ self::$version = $version;
68
+ }
69
+
70
+ /**
71
+ * Allowed types getter.
72
+ * @since 1.0.0
73
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
74
+ * @access protected
75
+ * @return array The allowed request types.
76
+ */
77
+ protected function get_allowed_types() {
78
+ return self::$allowed_types;
79
+ }
80
+
81
+ /**
82
+ * Checks if the user has any content published on the site. Including comments.
83
+ * @since 1.0.0
84
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
85
+ * @static
86
+ * @param WP_User/int $user The user object or the user ID.
87
+ * @return bool Whether the user has content or not.
88
+ */
89
+ static function user_has_content( $user ) {
90
+ if ( ! $user instanceof WP_User ) {
91
+ if ( ! is_int( $user ) ) {
92
+ return;
93
+ }
94
+ $user = get_user_by( 'ID', $user );
95
+ }
96
+
97
+ $post_types = get_post_types( array( 'public' => true ) );
98
+ foreach ( $post_types as $pt ) {
99
+ $post_count = count_user_posts( $user->ID, $pt);
100
+ if ( $post_count > 0 ) {
101
+ return true;
102
+ }
103
+ }
104
+
105
+ $comments = get_comments( array(
106
+ 'author_email' => $user->user_email,
107
+ 'include_unapproved' => true,
108
+ 'number' => 1,
109
+ 'count' => true,
110
+ ) );
111
+
112
+ if ( $comments ) {
113
+ return true;
114
+ }
115
+
116
+ $extra_checks = apply_filters( 'gdpr_user_has_content', false );
117
+
118
+ return $extra_checks;
119
+ }
120
+
121
+ /**
122
+ * Removes the user from the requests list.
123
+ * @since 1.0.0
124
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
125
+ * @access protected
126
+ * @param string $index The request key.
127
+ * @return bool Whether the user was removed from the requests list.
128
+ */
129
+ protected function remove_from_requests( $index ) {
130
+ $requests = ( array ) get_option( 'gdpr_requests', array() );
131
+ $index = sanitize_text_field( wp_unslash( $index ) );
132
+
133
+ if ( array_key_exists( $index, $requests ) ) {
134
+ unset( $requests[ $index ] );
135
+ update_option( 'gdpr_requests', $requests );
136
+ return true;
137
+ }
138
+
139
+ return false;
140
+ }
141
+
142
+ /**
143
+ * Set the user request as confirmed.
144
+ * Unschedules the cron jobs that clean up the requests that haven't been confirmed.
145
+ * @since 1.0.0
146
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
147
+ * @access protected
148
+ * @param string $key The request key.
149
+ * @return bool Whether the request was confirmed or not.
150
+ */
151
+ protected function confirm_request( $key ) {
152
+ $key = sanitize_text_field( wp_unslash( $key ) );
153
+ $requests = ( array ) get_option( 'gdpr_requests', array() );
154
+
155
+ if ( empty( $requests ) || ! isset( $requests[ $key ] ) ) {
156
+ return false;
157
+ }
158
+
159
+ $requests[ $key ]['confirmed'] = true;
160
+ $type = $requests[ $key ]['type'];
161
+ $email = $requests[ $key ]['email'];
162
+
163
+ $user = get_user_by( 'email', $email );
164
+
165
+ if ( $user instanceof WP_User ) {
166
+ $meta_key = self::$plugin_name . "_{$type}_key";
167
+ update_option( 'gdpr_requests', $requests );
168
+ delete_user_meta( $user->ID, $meta_key );
169
+ if ( $time = wp_next_scheduled( 'clean_gdpr_user_request_key', array( 'user_id' => $user->ID, 'meta_key' => $meta_key ) ) ) {
170
+ wp_unschedule_event( $time, 'clean_gdpr_user_request_key', array( 'user_id' => $user->ID, 'meta_key' => $meta_key ) );
171
+ }
172
+ }
173
+
174
+ return true;
175
+ }
176
+
177
+ /**
178
+ * The function the CRON job calls. It checks after a couple days if a request was confirmed or not.
179
+ * If it wasn't, the request gets removed.
180
+ * @since 1.0.0
181
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
182
+ * @param string $key The request key.
183
+ */
184
+ function clean_requests( $key ) {
185
+ $key = sanitize_text_field( $key );
186
+ $requests = ( array ) get_option( 'gdpr_requests', array() );
187
+
188
+ if ( array_key_exists( $key, $requests ) ) {
189
+ if ( ! $requests[ $key ]['confirmed'] ) {
190
+ unset( $requests[ $key ] );
191
+ update_option( 'gdpr_requests', $requests );
192
+ }
193
+ }
194
+ }
195
+
196
+ /**
197
+ * Whenever a user places a request, the request key is saved as a user meta for comparison.
198
+ * @since 1.0.0
199
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
200
+ * @param int $user_id The user ID.
201
+ * @param string $meta_key The user meta key.
202
+ */
203
+ function clean_user_request_key( $user_id, $meta_key ) {
204
+ $user_id = ( int ) $user_id;
205
+ $meta_key = sanitize_text_field( $meta_key );
206
+
207
+ $meta = get_user_meta( $user_id, $meta_key, true );
208
+
209
+ if ( $meta ) {
210
+ delete_user_meta( $user_id, $meta_key );
211
+ }
212
+
213
+ /* translators: Name of the usermeta */
214
+ GDPR_Audit_Log::log( $user_id, sprintf( esc_html__( 'User request expired. Removing %s user_meta.', 'gdpr' ), $meta_key ) );
215
+ }
216
+
217
+ /**
218
+ * Add a user to the request list. Set up the cleanup CRON job.
219
+ * @since 1.0.0
220
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
221
+ * @access protected
222
+ * @param string $email The requestant email.
223
+ * @param string $type The type of request.
224
+ * @param string $data Some types of request have an extra field. E.g. Complaint and Rectify data.
225
+ * @param string $confirmed If the request is confirmed or not.
226
+ */
227
+ protected function add_to_requests( $email, $type, $data = null, $confirmed = false ) {
228
+ $requests = ( array ) get_option( 'gdpr_requests', array() );
229
+
230
+ $email = sanitize_email( $email );
231
+ $type = sanitize_text_field( wp_unslash( $type ) );
232
+ $data = sanitize_textarea_field( wp_unslash( $data ) );
233
+
234
+ if ( ! in_array( $type, self::$allowed_types ) ) {
235
+ return false;
236
+ }
237
+
238
+ $key = wp_generate_password( 20, false );
239
+ $requests[ $key ] = array(
240
+ 'email' => $email,
241
+ 'date' => date( "F j, Y" ),
242
+ 'type' => $type,
243
+ 'data' => $data,
244
+ 'confirmed' => $confirmed
245
+ );
246
+
247
+ /**
248
+ * Remove user from the requests if it did not confirm in 2 days.
249
+ */
250
+ $user = get_user_by( 'email', $email );
251
+ if ( $user instanceof WP_User ) {
252
+ $meta_key = self::$plugin_name . '_' . $type . '_key';
253
+ update_user_meta( $user->ID, $meta_key, $key );
254
+ if ( $time = wp_next_scheduled( 'clean_gdpr_user_request_key', array( 'user_id' => $user->ID, 'meta_key' => $meta_key ) ) ) {
255
+ wp_unschedule_event( $time, 'clean_gdpr_user_request_key', array( 'user_id' => $user->ID, 'meta_key' => $meta_key ) );
256
+ }
257
+ wp_schedule_single_event( time() + 2 * DAY_IN_SECONDS, 'clean_gdpr_user_request_key', array( 'user_id' => $user->ID, 'meta_key' => $meta_key ) );
258
+ }
259
+
260
+ update_option( 'gdpr_requests', $requests );
261
+
262
+ return $key;
263
+ }
264
+
265
+ }
includes/class-gdpr.php ADDED
@@ -0,0 +1,659 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * The file that defines the core plugin class
5
+ *
6
+ * A class definition that includes attributes and functions used across both the
7
+ * public-facing side of the site and the admin area.
8
+ *
9
+ * @link https://trewknowledge.com
10
+ * @since 1.0.0
11
+ *
12
+ * @package GDPR
13
+ * @subpackage includes
14
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
15
+ */
16
+
17
+ /**
18
+ * The core plugin class.
19
+ *
20
+ * This is used to define internationalization, admin-specific hooks, and
21
+ * public-facing site hooks.
22
+ *
23
+ * Also maintains the unique identifier of this plugin as well as the current
24
+ * version of the plugin.
25
+ *
26
+ * @since 1.0.0
27
+ * @package GDPR
28
+ * @subpackage includes
29
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
30
+ */
31
+ class GDPR {
32
+
33
+ /**
34
+ * The unique identifier of this plugin.
35
+ *
36
+ * @since 1.0.0
37
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
38
+ * @access protected
39
+ * @var string $plugin_name The string used to uniquely identify this plugin.
40
+ */
41
+ protected $plugin_name;
42
+
43
+ /**
44
+ * The current version of the plugin.
45
+ *
46
+ * @since 1.0.0
47
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
48
+ * @access protected
49
+ * @var string $version The current version of the plugin.
50
+ */
51
+ protected $version;
52
+
53
+ /**
54
+ * Define the core functionality of the plugin.
55
+ *
56
+ * Set the plugin name and the plugin version that can be used throughout the plugin.
57
+ * Load the dependencies, define the locale, and set the hooks for the admin area and
58
+ * the public-facing side of the site.
59
+ *
60
+ * @since 1.0.0
61
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
62
+ */
63
+ public function __construct() {
64
+ if ( defined( 'GDPR_VERSION' ) ) {
65
+ $this->version = GDPR_VERSION;
66
+ } else {
67
+ $this->version = '1.0.0';
68
+ }
69
+ $this->plugin_name = 'gdpr';
70
+
71
+ $this->load_dependencies();
72
+ $this->define_common_hooks();
73
+ $this->define_admin_hooks();
74
+ $this->define_public_hooks();
75
+
76
+ if ( ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) OR ( defined( 'DOING_CRON' ) && DOING_CRON ) OR ( defined( 'DOING_AJAX' ) && DOING_AJAX ) OR ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST ) ) {
77
+ return;
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Load the required dependencies for this plugin.
83
+ *
84
+ * @since 1.0.0
85
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
86
+ * @access private
87
+ */
88
+ private function load_dependencies() {
89
+ /**
90
+ * The class responsible for adding help tabs.
91
+ */
92
+ require_once plugin_dir_path( dirname( __FILE__ ) ) . 'includes/class-gdpr-help.php';
93
+
94
+ /**
95
+ * The class responsible logging user actions.
96
+ */
97
+ require_once plugin_dir_path( dirname( __FILE__ ) ) . 'includes/class-gdpr-audit-log.php';
98
+
99
+ /**
100
+ * The class responsible for defining the telemetry post type.
101
+ */
102
+ require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/class-gdpr-telemetry.php';
103
+
104
+ /**
105
+ * The class responsible for defining the requests section of the plugin.
106
+ */
107
+ require_once plugin_dir_path( dirname( __FILE__ ) ) . 'includes/class-gdpr-requests.php';
108
+
109
+ /**
110
+ * The class responsible for defining the admin facing requests section of the plugin.
111
+ */
112
+ require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/class-gdpr-requests-admin.php';
113
+
114
+ /**
115
+ * The class responsible for defining the admin facing requests section of the plugin.
116
+ */
117
+ require_once plugin_dir_path( dirname( __FILE__ ) ) . 'public/class-gdpr-requests-public.php';
118
+
119
+ /**
120
+ * The class responsible for locating the email templates and sending emails.
121
+ */
122
+ require_once plugin_dir_path( dirname( __FILE__ ) ) . 'includes/class-gdpr-email.php';
123
+
124
+ /**
125
+ * The class responsible for defining all actions that occur in the admin area.
126
+ */
127
+ require_once plugin_dir_path( dirname( __FILE__ ) ) . 'admin/class-gdpr-admin.php';
128
+
129
+ /**
130
+ * The class responsible for defining all actions that occur in the public-facing
131
+ * side of the site.
132
+ */
133
+ require_once plugin_dir_path( dirname( __FILE__ ) ) . 'public/class-gdpr-public.php';
134
+
135
+ }
136
+
137
+ /**
138
+ * Define the locale for this plugin for internationalization.
139
+ * @since 1.0.0
140
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
141
+ */
142
+ public function set_locale() {
143
+
144
+ load_plugin_textdomain(
145
+ 'gdpr',
146
+ false,
147
+ plugin_dir_url( dirname( __FILE__ ) ) . 'languages/'
148
+ );
149
+
150
+ }
151
+
152
+ /**
153
+ * Register all of the common hooks.
154
+ * @since 1.0.0
155
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
156
+ * @access private
157
+ */
158
+ private function define_common_hooks() {
159
+ add_action( 'wp_ajax_gdpr_generate_data_export', array( $this, 'export_data' ) );
160
+ }
161
+
162
+ /**
163
+ * Register all of the hooks related to the admin area functionality
164
+ * of the plugin.
165
+ *
166
+ * @since 1.0.0
167
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
168
+ * @access private
169
+ */
170
+ private function define_admin_hooks() {
171
+
172
+ $plugin_admin = new GDPR_Admin( $this->get_plugin_name(), $this->get_version() );
173
+ $requests_admin = new GDPR_Requests_Admin( $this->get_plugin_name(), $this->get_version() );
174
+ $telemetry = new GDPR_Telemetry( $this->get_plugin_name(), $this->get_version() );
175
+ $requests = new GDPR_Requests( $this->get_plugin_name(), $this->get_version() );
176
+ $plugin_emails = new GDPR_Email();
177
+ $woo_add_to_registration = get_option( 'gdpr_add_consent_checkboxes_registration', false );
178
+ $woo_add_to_checkout = get_option( 'gdpr_add_consent_checkboxes_checkout', false );
179
+
180
+ add_filter( 'nonce_user_logged_out', array( $this, 'woo_nonce_fix' ), 100, 2 );
181
+ add_action( 'plugins_loaded', array( $this, 'set_locale' ) );
182
+ add_action( 'bp_account_details_fields', array( __CLASS__, 'consent_checkboxes' ) );
183
+ if ( $woo_add_to_registration ) {
184
+ add_action( 'woocommerce_register_form', array( __CLASS__, 'consent_checkboxes' ) );
185
+ }
186
+ if ( $woo_add_to_checkout ) {
187
+ add_action( 'woocommerce_checkout_update_user_meta', array( $plugin_admin, 'woocommerce_checkout_save_consent' ), 10, 2 );
188
+ add_filter( 'woocommerce_checkout_fields', array( $plugin_admin, 'woocommerce_consent_checkboxes' ) );
189
+ }
190
+ add_action( 'show_user_profile', array( $plugin_admin, 'edit_user_profile' ) );
191
+ add_action( 'personal_options_update', array( $plugin_admin, 'user_profile_update' ) );
192
+ add_action( 'admin_notices', array( $plugin_admin, 'privacy_policy_page_missing' ) );
193
+ add_action( 'admin_notices', array( $plugin_admin, 'privacy_policy_updated_notice' ) );
194
+ add_action( 'wp_ajax_ignore_privacy_policy_update', array( $plugin_admin, 'ignore_privacy_policy_update' ) );
195
+ add_action( 'admin_post_seek_consent', array( $plugin_admin, 'seek_consent' ) );
196
+ add_action( 'publish_page', array( $plugin_admin, 'privacy_policy_updated' ), 10, 2 );
197
+ add_action( 'admin_enqueue_scripts', array( $plugin_admin, 'enqueue_styles' ) );
198
+ add_action( 'admin_enqueue_scripts', array( $plugin_admin, 'enqueue_scripts' ) );
199
+ add_action( 'admin_menu', array( $plugin_admin, 'add_menu' ) );
200
+ add_action( 'admin_init', array( $plugin_admin, 'register_settings' ) );
201
+ add_action( 'register_form', array( __CLASS__, 'consent_checkboxes' ) );
202
+ add_action( 'registration_errors', array( $plugin_admin, 'registration_errors' ), 10, 3 );
203
+ add_action( 'user_register', array( __CLASS__, 'save_user_consent_on_registration' ) );
204
+ add_action( 'wp_ajax_gdpr_access_data', array( $plugin_admin, 'access_data' ) );
205
+ add_action( 'wp_ajax_gdpr_audit_log', array( $plugin_admin, 'audit_log' ) );
206
+ add_action( 'admin_post_gdpr_data_breach', array( $plugin_admin, 'send_data_breach_confirmation_email' ) );
207
+ add_action( 'clean_gdpr_data_breach_request', array( $plugin_admin, 'clean_data_breach_request' ), 10, 2 ); // CRON JOB
208
+ add_action( 'telemetry_cleanup', array( $plugin_admin, 'telemetry_cleanup' ) ); // CRON JOB
209
+
210
+ add_action( 'admin_post_gdpr_delete_user', array( $requests_admin, 'delete_user' ) );
211
+ add_action( 'admin_post_gdpr_cancel_request', array( $requests_admin, 'cancel_request' ) );
212
+ add_action( 'admin_post_gdpr_add_to_deletion_requests', array( $requests_admin, 'add_to_deletion_requests' ) );
213
+ add_action( 'admin_post_gdpr_mark_resolved', array( $requests_admin, 'mark_resolved' ) );
214
+ add_action( 'wp_ajax_gdpr_anonymize_comments', array( $requests_admin, 'anonymize_comments' ) );
215
+ add_action( 'wp_ajax_gdpr_reassign_content', array( $requests_admin, 'reassign_content' ) );
216
+
217
+ add_action( 'init', array( $telemetry, 'register_post_type' ) );
218
+ add_filter( 'http_api_debug', array( $telemetry, 'log_request' ), 10, 5 );
219
+ add_filter( 'manage_telemetry_posts_columns', array( $telemetry, 'manage_columns' ) );
220
+ add_filter( 'manage_telemetry_posts_custom_column', array( $telemetry, 'custom_column' ), 10, 2 );
221
+ add_filter( 'restrict_manage_posts', array( $telemetry, 'actions_above_table' ) );
222
+ add_filter( 'views_edit-telemetry', '__return_null' );
223
+
224
+ // CRON JOBS
225
+ add_action( 'clean_gdpr_requests', array( $requests, 'clean_requests' ) );
226
+ add_action( 'clean_gdpr_user_request_key', array( $requests, 'clean_user_request_key' ), 10, 2 );
227
+
228
+ add_action( 'send_data_breach_emails', array( $plugin_emails, 'send_data_breach_emails' ), 10, 2 );
229
+ }
230
+
231
+ /**
232
+ * Fixes nonce manipulation made by Woocommerce.
233
+ * @param int $user_id The user id.
234
+ * @param string $action The nonce Action.
235
+ * @return int The user id.
236
+ */
237
+ function woo_nonce_fix( $user_id, $action ) {
238
+ if ( ( 0 !== $user_id ) && $action && ( false !== strpos( $action, 'gdpr-' ) ) ) {
239
+ $user_id = 0;
240
+ }
241
+
242
+ return $user_id;
243
+ }
244
+
245
+ /**
246
+ * Register all of the hooks related to the public-facing functionality
247
+ * of the plugin.
248
+ *
249
+ * @since 1.0.0
250
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
251
+ * @access private
252
+ */
253
+ private function define_public_hooks() {
254
+
255
+ $plugin_public = new GDPR_Public( $this->get_plugin_name(), $this->get_version() );
256
+ $requests_public = new GDPR_Requests_Public( $this->get_plugin_name(), $this->get_version() );
257
+
258
+ add_action( 'wp_enqueue_scripts', array( $plugin_public, 'enqueue_styles' ) );
259
+ add_action( 'wp_enqueue_scripts', array( $plugin_public, 'enqueue_scripts' ) );
260
+ add_action( 'init', array( $plugin_public, 'set_plugin_cookies' ) );
261
+ add_action( 'wp_footer', array( $plugin_public, 'overlay' ) );
262
+ add_action( 'wp_footer', array( $plugin_public, 'privacy_bar' ) );
263
+ add_action( 'wp_footer', array( $plugin_public, 'is_consent_needed' ) );
264
+ add_action( 'wp_footer', array( $plugin_public, 'privacy_preferences_modal' ) );
265
+ add_action( 'wp_footer', array( $plugin_public, 'confirmation_screens' ) );
266
+ add_action( 'wp_ajax_disagree_with_terms', array( $plugin_public, 'logout' ) );
267
+ add_action( 'wp_ajax_agree_with_terms', array( $plugin_public, 'agree_with_terms' ) );
268
+ add_action( 'admin_post_gdpr_update_privacy_preferences', array( $plugin_public, 'update_privacy_preferences' ) );
269
+ add_action( 'admin_post_nopriv_gdpr_update_privacy_preferences', array( $plugin_public, 'update_privacy_preferences' ) );
270
+
271
+ add_action( 'wp', array( $requests_public, 'request_confirmed' ) );
272
+ add_action( 'admin_post_gdpr_send_request_email', array( $requests_public, 'send_request_email' ) );
273
+ add_action( 'admin_post_nopriv_gdpr_send_request_email', array( $requests_public, 'send_request_email' ) );
274
+ }
275
+
276
+ /**
277
+ * Checks in an array if a value is found using LIKE instead of =.
278
+ * @since 1.4.3
279
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
280
+ * @return Bool
281
+ */
282
+ public static function similar_in_array( $needle, $haystack ) {
283
+ foreach ( $haystack as $value ) {
284
+ if( stripos( strtolower($value) , strtolower($needle) ) !== false ) {
285
+ return true;
286
+ }
287
+ }
288
+
289
+ return false;
290
+ }
291
+
292
+ /**
293
+ * Save the extra fields on a successful registration.
294
+ * @since 1.0.0
295
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
296
+ * @param int $user_id The user ID.
297
+ */
298
+ public static function save_user_consent_on_registration( $user_id ) {
299
+ GDPR_Audit_Log::log( $user_id, esc_html__( 'User registered to the site.', 'gdpr' ) );
300
+
301
+ if ( isset( $_POST['user_consents'] ) ) {
302
+
303
+ $consents = array_map( 'sanitize_text_field', array_keys( $_POST['user_consents'] ) );
304
+ foreach ( $consents as $consent ) {
305
+ /* translators: Name of consent */
306
+ GDPR_Audit_Log::log( $user_id, sprintf( esc_html__( 'User gave explicit consent to %s', 'gdpr' ), $consent ) );
307
+ add_user_meta( $user_id, 'gdpr_consents', $consent );
308
+ }
309
+ setcookie( "gdpr[consent_types]", json_encode( $consents ), time() + YEAR_IN_SECONDS, "/" );
310
+ }
311
+ }
312
+
313
+ /**
314
+ * Returns the consent checkboxes to be used across the site.
315
+ * @since 1.2.0
316
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
317
+ */
318
+ public static function get_consent_checkboxes() {
319
+ $consent_types = get_option( 'gdpr_consent_types', array() );
320
+ $sent_extras = ( isset( $_POST['user_consents'] ) ) ? $_POST['user_consents'] : array();
321
+ $allowed_html = array(
322
+ 'a' => array(
323
+ 'href' => true,
324
+ 'title' => true,
325
+ 'target' => true,
326
+ ),
327
+ );
328
+
329
+ ob_start();
330
+ foreach ( $consent_types as $key => $consent ) {
331
+ $required = ( isset( $consent['required'] ) && $consent['required'] ) ? 'required' : '';
332
+ $checked = ( isset( $sent_extras[ $key ] ) ) ? checked( $sent_extras[ $key ], 1, false ) : '';
333
+ echo '<p>' .
334
+ '<input type="checkbox" name="user_consents[' . esc_attr( $key ) . ']" id="' . esc_attr( $key ) . '-consent" value="1" ' . $required . ' ' . $checked . '>' .
335
+ '<label for="' . esc_attr( $key ) . '-consent">' . wp_kses( $consent['registration'], $allowed_html ) . '</label>' .
336
+ '</p>';
337
+ }
338
+
339
+ return ob_get_clean();
340
+ }
341
+
342
+ /**
343
+ * Renders consent checkboxes to be used across the site.
344
+ * @since 1.1.4
345
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
346
+ */
347
+ public static function consent_checkboxes() {
348
+ echo self::get_consent_checkboxes();
349
+ }
350
+
351
+ /**
352
+ * Get user meta for exporting.
353
+ * @since 1.0.0
354
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
355
+ * @static
356
+ * @param int $user_id The user ID.
357
+ * @return array The user meta minus not important metas.
358
+ */
359
+ static function get_user_meta( $user_id ) {
360
+ $usermeta = get_user_meta( $user_id );
361
+ $remove_metadata = array(
362
+ 'nickname',
363
+ 'first_name',
364
+ 'last_name',
365
+ 'description',
366
+ 'rich_editing',
367
+ 'syntax_highlighting',
368
+ 'comment_shortcuts',
369
+ 'admin_color',
370
+ 'use_ssl',
371
+ 'show_admin_bar_front',
372
+ 'wp_capabilities',
373
+ 'wp_user_level',
374
+ 'gdpr_consents',
375
+ 'gdpr_audit_log',
376
+ 'dismissed_wp_pointers',
377
+ 'gdpr_delete_key',
378
+ 'gdpr_rectify_key',
379
+ 'gdpr_complaint_key',
380
+ 'gdpr_export-data_key',
381
+
382
+ );
383
+
384
+ return array_diff_key( $usermeta, array_flip( $remove_metadata ) );
385
+ }
386
+
387
+ /**
388
+ * Generates the export in JSON or XML formats.
389
+ * @since 1.0.0
390
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
391
+ * @static
392
+ * @param string $email The user email.
393
+ * @param string $format Either XML or JSON.
394
+ * @return string Returns the file as string.
395
+ */
396
+ static function generate_export( $email, $format ) {
397
+
398
+ $email = sanitize_email( $email );
399
+ $user = get_user_by( 'email', $email );
400
+
401
+ if ( ! $user instanceof WP_User ) {
402
+ return false;
403
+ }
404
+
405
+ $usermeta = self::get_user_meta( $user->ID );
406
+ $comments = get_comments( array(
407
+ 'author_email' => $user->user_email,
408
+ 'include_unapproved' => true,
409
+ ) );
410
+ $user_consents = get_user_meta( $user->ID, 'gdpr_consents' );
411
+ $extra_content = apply_filters( 'gdpr_export_data_extra_tables', '', $email );
412
+
413
+ switch ( strtolower( $format ) ) {
414
+ case 'json':
415
+ $metadata = array();
416
+
417
+ foreach ( $usermeta as $k => $v ) {
418
+ $metadata[ $k ] = array();
419
+ foreach ( $v as $value ) {
420
+ if ( is_serialized( $value ) ) {
421
+ $metadata[ $k ][] = maybe_unserialize( $value );
422
+ } else {
423
+ $metadata[ $k ] = $value;
424
+ }
425
+ }
426
+ }
427
+
428
+ $comments_array = array();
429
+ if ( ! empty( $comments ) ) {
430
+ foreach ( $comments as $k => $v ) {
431
+ $comments_array[ $k ] = array(
432
+ 'comment_author' => $v->comment_author,
433
+ 'comment_author_email' => $v->comment_author_email,
434
+ 'comment_author_url' => $v->comment_author_url,
435
+ 'comment_author_IP' => $v->comment_author_IP,
436
+ 'comment_date' => $v->comment_date,
437
+ 'comment_agent' => $v->comment_agent,
438
+ 'comment_content' => $v->comment_content,
439
+ );
440
+ }
441
+ }
442
+
443
+ $json = array(
444
+ 'Personal Information' => array(
445
+ 'Username' => $user->user_login,
446
+ 'First name' => $user->first_name,
447
+ 'Last name' => $user->last_name,
448
+ 'Email' => $user->user_email,
449
+ 'Nickname' => $user->nickname,
450
+ 'Display name' => $user->display_name,
451
+ 'Description' => $user->description,
452
+ 'Website' => $user->user_url,
453
+ ),
454
+ 'Consents' => $user_consents,
455
+ 'Metadata' => $metadata,
456
+ 'Comments' => $comments_array,
457
+ );
458
+
459
+ if ( $extra_content ) {
460
+ $json[ $extra_content['name'] ] = $extra_content['content'];
461
+ }
462
+ return json_encode( $json );
463
+ break;
464
+ case 'md':
465
+ case 'markdown':
466
+ # code...
467
+ break;
468
+
469
+ default: // XML
470
+ $dom = new DomDocument( '1.0', 'ISO-8859-1' );
471
+ $personal_info = $dom->createElement( 'Personal_Information' );
472
+ $dom->appendChild( $personal_info );
473
+ $personal_info->appendChild( $dom->createElement( 'Username', $user->user_login ) );
474
+ $personal_info->appendChild( $dom->createElement( 'First_Name', $user->first_name ) );
475
+ $personal_info->appendChild( $dom->createElement( 'Last_Name', $user->last_name ) );
476
+ $personal_info->appendChild( $dom->createElement( 'Email', $user->user_email ) );
477
+ $personal_info->appendChild( $dom->createElement( 'Nickname', $user->nickname ) );
478
+ $personal_info->appendChild( $dom->createElement( 'Display_Name', $user->display_name ) );
479
+ $personal_info->appendChild( $dom->createElement( 'Description', $user->description ) );
480
+ $personal_info->appendChild( $dom->createElement( 'Website', $user->user_url ) );
481
+
482
+ if ( ! empty( $user_consents ) ) {
483
+ $consents = $dom->createElement( 'Consents' );
484
+ $dom->appendChild( $consents );
485
+ foreach ( $user_consents as $consent_item ) {
486
+ $consents->appendChild( $dom->createElement( 'consent', $consent_item ) );
487
+ }
488
+ }
489
+
490
+ if ( ! empty( $comments ) ) {
491
+ $comments_node = $dom->createElement( 'Comments' );
492
+ $dom->appendChild( $comments_node );
493
+ foreach ( $comments as $k => $v ) {
494
+ $single_comment = $dom->createElement( 'Comment' );
495
+ $comments_node->appendChild( $single_comment );
496
+ $single_comment->appendChild( $dom->createElement( 'comment_author', htmlspecialchars( $v->comment_author ) ) );
497
+ $single_comment->appendChild( $dom->createElement( 'comment_author_email', htmlspecialchars( $v->comment_author_email ) ) );
498
+ $single_comment->appendChild( $dom->createElement( 'comment_author_url', htmlspecialchars( $v->comment_author_url ) ) );
499
+ $single_comment->appendChild( $dom->createElement( 'comment_author_IP', htmlspecialchars( $v->comment_author_IP ) ) );
500
+ $single_comment->appendChild( $dom->createElement( 'comment_date', htmlspecialchars( $v->comment_date ) ) );
501
+ $single_comment->appendChild( $dom->createElement( 'comment_agent', htmlspecialchars( $v->comment_agent ) ) );
502
+ $single_comment->appendChild( $dom->createElement( 'comment_content', htmlspecialchars( $v->comment_content ) ) );
503
+ }
504
+ }
505
+
506
+ $meta_data = $dom->createElement( 'Metadata' );
507
+ $dom->appendChild( $meta_data );
508
+
509
+ foreach ( $usermeta as $k => $v ) {
510
+ $k = is_numeric( substr( $k, 0, 1 ) ) ? '_' . $k : $k;
511
+ $key = $dom->createElement( htmlspecialchars( $k ) );
512
+ $meta_data->appendChild( $key );
513
+ foreach ( $v as $value ) {
514
+ $key->appendChild( $dom->createElement( 'item', htmlspecialchars( $value ) ) );
515
+ }
516
+ }
517
+
518
+ if ( $extra_content ) {
519
+ $extra = $dom->createElement( $extra_content['name'] );
520
+ $dom->appendChild( $extra );
521
+ foreach ( $extra_content['content'] as $key => $obj ) {
522
+ $item = $extra->appendChild( $dom->createElement( 'item' ) );
523
+ foreach ( $obj as $k => $value ) {
524
+ $item->appendChild( $dom->createElement( $k, ( is_object( $value ) || is_array( $value ) ) ? serialize( (array) $value ) : $value ) );
525
+ }
526
+ }
527
+ }
528
+
529
+ $dom->preserveWhiteSpace = false;
530
+ $dom->formatOutput = true;
531
+ return $dom->saveXML();
532
+ break;
533
+ }
534
+
535
+ return false;
536
+
537
+ }
538
+
539
+ /**
540
+ * Export the generated export file.
541
+ * @since 1.0.0
542
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
543
+ */
544
+ function export_data() {
545
+ if ( ! isset( $_POST['nonce'], $_POST['email'], $_POST['type'] ) || ! wp_verify_nonce( sanitize_key( wp_unslash( $_POST['nonce'] ) ), 'gdpr-export-data' ) ) {
546
+ wp_send_json_error();
547
+ }
548
+
549
+ $type = sanitize_text_field( wp_unslash( $_POST['type'] ) );
550
+ $email = sanitize_email( $_POST['email'] );
551
+ $user = get_user_by( 'email', $email );
552
+
553
+ if ( ! $user instanceof WP_User ) {
554
+ wp_send_json_error();
555
+ }
556
+
557
+ $export = self::generate_export( $email, $type );
558
+ if ( $export ) {
559
+ wp_send_json_success( $export );
560
+ }
561
+
562
+ wp_send_json_error();
563
+ }
564
+
565
+ /**
566
+ * Save a consent to the user meta.
567
+ * @since 1.1.4
568
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
569
+ * @param integer $user_id The user ID.
570
+ * @param string $consent The consent ID.
571
+ * @return void
572
+ */
573
+ public static function save_consent( $user_id, $consent ) {
574
+ $registered_consent = get_option( 'gdpr_consent_types', array( 'privacy-policy' ) );
575
+ $consent_ids = array_keys( $registered_consent );
576
+ $user = get_user_by( 'ID', $user_id );
577
+ $consent = sanitize_text_field( wp_unslash( $consent ) );
578
+
579
+ if ( $user ) {
580
+ $user_consent = get_user_meta( $user_id, 'gdpr_consents' );
581
+ if ( in_array( $consent, $consent_ids ) && ! in_array( $consent, $user_consent ) ) {
582
+ add_user_meta( $user_id, 'gdpr_consents', $consent );
583
+ $user_consent[] = $consent;
584
+ setcookie( "gdpr[consent_types]", json_encode( $user_consent ), time() + YEAR_IN_SECONDS, "/" );
585
+ return true;
586
+ }
587
+ }
588
+
589
+ return false;
590
+ }
591
+
592
+ /**
593
+ * Remove a user consent.
594
+ * @since 1.1.4
595
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
596
+ * @param integer $user_id The user ID.
597
+ * @param string $consent The consent ID.
598
+ * @return void
599
+ */
600
+ public static function remove_consent( $user_id, $consent ) {
601
+ $user = get_user_by( 'ID', $user_id );
602
+
603
+ if ( $user ) {
604
+ $user_consent = get_user_meta( $user_id, 'gdpr_consents' );
605
+
606
+ $consent = sanitize_text_field( wp_unslash( $consent ) );
607
+ $key = array_search( $consent, $user_consent );
608
+ if ( false !== $key ) {
609
+ delete_user_meta( $user_id, 'gdpr_consents', $consent );
610
+ unset( $user_consent[ $key ] );
611
+ setcookie( "gdpr[consent_types]", json_encode( $user_consent ), time() + YEAR_IN_SECONDS, "/" );
612
+ return true;
613
+ }
614
+ }
615
+
616
+ return false;
617
+ }
618
+
619
+
620
+ /**
621
+ * Generates a random 6 digit pin.
622
+ * This pin is necessary to use with the audit log files.
623
+ *
624
+ * @since 1.0.0
625
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
626
+ * @static
627
+ * @param integer $length Number of digits.
628
+ * @return string Returns the generated pin
629
+ */
630
+ public static function generate_pin( $length = 6 ) {
631
+ $bytes = openssl_random_pseudo_bytes( $length / 2 );
632
+ return strtoupper( bin2hex( $bytes ) );
633
+ }
634
+
635
+
636
+ /**
637
+ * The name of the plugin used to uniquely identify it within the context of
638
+ * WordPress and to define internationalization functionality.
639
+ *
640
+ * @since 1.0.0
641
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
642
+ * @return string The name of the plugin.
643
+ */
644
+ public function get_plugin_name() {
645
+ return $this->plugin_name;
646
+ }
647
+
648
+ /**
649
+ * Retrieve the version number of the plugin.
650
+ *
651
+ * @since 1.0.0
652
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
653
+ * @return string The version number of the plugin.
654
+ */
655
+ public function get_version() {
656
+ return $this->version;
657
+ }
658
+
659
+ }
includes/helper-functions.php ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The plugin helper functions
4
+ *
5
+ * @link https://trewknowledge.com
6
+ * @since 1.0.0
7
+ *
8
+ * @package GDPR
9
+ * @subpackage public
10
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
11
+ */
12
+
13
+ /**
14
+ * Adds a button to re-open the cookie preferences modal.
15
+ * @since 1.0.0
16
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
17
+ * @param string $text The button text.
18
+ * @param string $type The type of preferences. Possible options are `cookies` or `consents`
19
+ */
20
+ function gdpr_preferences( $text ) {
21
+ echo '<button type="button" class="gdpr-preferences">' . esc_html( $text ) . '</button>';
22
+ }
23
+
24
+ function gdpr_preferences_shortcode( $atts ) {
25
+ $atts = shortcode_atts( array(
26
+ 'text' => esc_html__( 'Privacy Preferences', 'gdpr' ),
27
+ ), $atts, 'gdpr_preferences' );
28
+
29
+ ob_start();
30
+ gdpr_preferences( $atts['text'] );
31
+ return ob_get_clean();
32
+ }
33
+
34
+ add_shortcode( 'gdpr_preferences', 'gdpr_preferences_shortcode' );
35
+
36
+ /**
37
+ * Load the request forms.
38
+ * @since 1.0.0
39
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
40
+ * @param string $type The type of request.
41
+ */
42
+ function gdpr_request_form( $type ) {
43
+ echo GDPR_Requests_Public::request_form( $type );
44
+ }
45
+
46
+ /**
47
+ * Create the request form shortcode.
48
+ * @since 1.0.0
49
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
50
+ * @param string $atts Shortcode attributes.
51
+ */
52
+ function gdpr_request_form_shortcode( $atts ) {
53
+ $atts = shortcode_atts( array(
54
+ 'type' => '',
55
+ ), $atts, 'gdpr_request_form' );
56
+
57
+ return GDPR_Requests_Public::request_form( $atts['type'] );
58
+ }
59
+
60
+ add_shortcode( 'gdpr_request_form', 'gdpr_request_form_shortcode' );
61
+
62
+ /**
63
+ * Checks if a cookie is allowed
64
+ * @since 1.0.0
65
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
66
+ * @param string $cookie_name The cookie name.
67
+ * @return bool Whether the cookie is allowed or not.
68
+ */
69
+ function is_allowed_cookie( $cookie_name ) {
70
+ if ( isset( $_COOKIE['gdpr']['allowed_cookies'] ) ) {
71
+ $allowed_cookies = json_decode( wp_unslash( $_COOKIE['gdpr']['allowed_cookies'] ), true );
72
+ $name = preg_quote( $cookie_name, '~' );
73
+ $result = preg_grep( '~' . $name . '~', $allowed_cookies );
74
+ if ( in_array( $cookie_name, $allowed_cookies ) || ! empty( $result ) ) {
75
+ return true;
76
+ }
77
+ }
78
+
79
+ return false;
80
+ }
81
+
82
+ function gdpr_deprecated_function( $function, $version, $replacement = null ) {
83
+ if ( defined( 'DOING_AJAX' ) ) {
84
+ do_action( 'deprecated_function_run', $function, $replacement, $version );
85
+ $log_string = "The {$function} function is deprecated since version {$version}.";
86
+ $log_string .= $replacement ? " Replace with {$replacement}." : '';
87
+ } else {
88
+ _deprecated_function( $function, $version, $replacement );
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Checks if a user gave consent.
94
+ * @since 1.0.0
95
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
96
+ * @param string $consent The consent id.
97
+ * @return bool Whether the user gave consent or not.
98
+ */
99
+ function have_consent( $consent ) {
100
+ gdpr_deprecated_function( 'have_consent', '1.1.0', 'has_consent' );
101
+ return has_consent( $consent );
102
+ }
103
+
104
+ function has_consent( $consent ) {
105
+
106
+ if ( is_user_logged_in() ) {
107
+ $user = wp_get_current_user();
108
+ $consents = (array) get_user_meta( $user->ID, 'gdpr_consents' );
109
+ } else if ( isset( $_COOKIE['gdpr']['consent_types'] ) && ! empty( $_COOKIE['gdpr']['consent_types'] ) ) {
110
+ $consents = array_map( 'sanitize_text_field', (array) json_decode( wp_unslash( $_COOKIE['gdpr']['consent_types'] ) ) );
111
+ }
112
+
113
+ if ( isset( $consents ) && ! empty( $consents ) ) {
114
+ if ( in_array( $consent, $consents ) ) {
115
+ return true;
116
+ }
117
+ }
118
+
119
+ return false;
120
+ }
includes/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden
index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden
languages/gdpr.pot ADDED
@@ -0,0 +1,1243 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Copyright (C) 2018 gdpr
2
+ # This file is distributed under the same license as the gdpr package.
3
+ msgid ""
4
+ msgstr ""
5
+ "Project-Id-Version: gdpr\n"
6
+ "MIME-Version: 1.0\n"
7
+ "Content-Type: text/plain; charset=UTF-8\n"
8
+ "Content-Transfer-Encoding: 8bit\n"
9
+ "X-Poedit-Basepath: ..\n"
10
+ "X-Poedit-KeywordsList: __;_e;_ex:1,2c;_n:1,2;_n_noop:1,2;_nx:1,2,4c;_nx_noop:1,2,3c;_x:1,2c;esc_attr__;esc_attr_e;esc_attr_x:1,2c;esc_html__;esc_html_e;esc_html_x:1,2c\n"
11
+ "X-Poedit-SearchPath-0: .\n"
12
+ "X-Poedit-SearchPathExcluded-0: *.js\n"
13
+ "X-Poedit-SourceCharset: UTF-8\n"
14
+ "Plural-Forms: nplurals=2; plural=(n != 1);\n"
15
+
16
+ #: admin/class-gdpr-admin.php:100, admin/class-gdpr-admin.php:111
17
+ msgid "GDPR"
18
+ msgstr ""
19
+
20
+ #: admin/class-gdpr-admin.php:118, admin/partials/requests.php:18
21
+ msgid "Requests"
22
+ msgstr ""
23
+
24
+ #: admin/class-gdpr-admin.php:124, admin/partials/tools.php:44
25
+ msgid "Tools"
26
+ msgstr ""
27
+
28
+ #: admin/class-gdpr-admin.php:130, admin/partials/settings.php:2
29
+ msgid "Settings"
30
+ msgstr ""
31
+
32
+ #: admin/class-gdpr-admin.php:271, admin/partials/settings.php:18
33
+ msgid "General"
34
+ msgstr ""
35
+
36
+ #: admin/class-gdpr-admin.php:272, admin/partials/settings.php:168
37
+ msgid "Cookies"
38
+ msgstr ""
39
+
40
+ #: admin/class-gdpr-admin.php:273, admin/partials/settings.php:248
41
+ msgid "Consents"
42
+ msgstr ""
43
+
44
+ #: admin/class-gdpr-admin.php:304, includes/class-gdpr-help.php:38, includes/class-gdpr-help.php:43, admin/partials/requests.php:32
45
+ msgid "Rectify Data"
46
+ msgstr ""
47
+
48
+ #: admin/class-gdpr-admin.php:308
49
+ msgid "Complaint"
50
+ msgstr ""
51
+
52
+ #: admin/class-gdpr-admin.php:312, includes/class-gdpr-help.php:56
53
+ msgid "Erasure"
54
+ msgstr ""
55
+
56
+ #: admin/class-gdpr-admin.php:329, includes/class-gdpr-help.php:81, includes/class-gdpr-help.php:85, admin/partials/tools.php:55
57
+ msgid "Access Data"
58
+ msgstr ""
59
+
60
+ #: admin/class-gdpr-admin.php:330, includes/class-gdpr-help.php:95, admin/partials/tools.php:72
61
+ msgid "Data Breach"
62
+ msgstr ""
63
+
64
+ #: admin/class-gdpr-admin.php:331, includes/class-gdpr-help.php:99, includes/class-gdpr-help.php:106, admin/partials/tools.php:117
65
+ msgid "Audit Log"
66
+ msgstr ""
67
+
68
+ #: admin/class-gdpr-admin.php:403
69
+ msgid "Consent Given"
70
+ msgstr ""
71
+
72
+ #: admin/class-gdpr-admin.php:407
73
+ msgid "Consent ID"
74
+ msgstr ""
75
+
76
+ #: admin/class-gdpr-admin.php:419, admin/partials/requests.php:275
77
+ msgid "Comments"
78
+ msgstr ""
79
+
80
+ #: admin/class-gdpr-admin.php:424
81
+ msgid "Comment Field"
82
+ msgstr ""
83
+
84
+ #: admin/class-gdpr-admin.php:425
85
+ msgid "Comment Data"
86
+ msgstr ""
87
+
88
+ #: admin/class-gdpr-admin.php:461
89
+ msgid "Metadata"
90
+ msgstr ""
91
+
92
+ #: admin/class-gdpr-admin.php:465
93
+ msgid "Name"
94
+ msgstr ""
95
+
96
+ #: admin/class-gdpr-admin.php:466
97
+ msgid "Value"
98
+ msgstr ""
99
+
100
+ #: admin/class-gdpr-admin.php:515
101
+ msgid "No logs found for this email."
102
+ msgstr ""
103
+
104
+ #: admin/class-gdpr-admin.php:534
105
+ msgid "[GDPR] You must select a Privacy Policy Page."
106
+ msgstr ""
107
+
108
+ #: admin/class-gdpr-admin.php:537
109
+ msgid "Select your Privacy Policy page"
110
+ msgstr ""
111
+
112
+ #: admin/class-gdpr-admin.php:556
113
+ msgid "Your Privacy Policy have been updated. In case this was not a small typo fix, you must ask users for explicit consent again."
114
+ msgstr ""
115
+
116
+ #: admin/class-gdpr-admin.php:562
117
+ msgid "Ask for consent"
118
+ msgstr ""
119
+
120
+ #: admin/class-gdpr-admin.php:569
121
+ msgid "Ignore"
122
+ msgstr ""
123
+
124
+ #: admin/class-gdpr-admin.php:583, admin/class-gdpr-admin.php:722, admin/class-gdpr-admin.php:784, public/class-gdpr-public.php:189, public/class-gdpr-public.php:282, public/class-gdpr-public.php:296
125
+ msgid "We could not verify the the security token. Please try again."
126
+ msgstr ""
127
+
128
+ #: admin/class-gdpr-admin.php:595
129
+ msgid "One or more required fields are missing. Please try again."
130
+ msgstr ""
131
+
132
+ #: admin/class-gdpr-admin.php:642
133
+ msgid "Data breach notification has been initialized. An email confirmation has been sent to the website controller."
134
+ msgstr ""
135
+
136
+ #: admin/class-gdpr-admin.php:705
137
+ msgid "ERROR"
138
+ msgstr ""
139
+
140
+ #: admin/class-gdpr-admin.php:707
141
+ msgid "is a required consent"
142
+ msgstr ""
143
+
144
+ #: admin/class-gdpr-admin.php:734
145
+ msgid "Privacy Policy has been updated. Removing the Privacy Policy consent and requesting new consent."
146
+ msgstr ""
147
+
148
+ #: admin/class-gdpr-admin.php:739
149
+ msgid "Users will have to consent to the updated privacy policy on login."
150
+ msgstr ""
151
+
152
+ #: admin/class-gdpr-admin.php:804, includes/class-gdpr-help.php:161, public/partials/privacy-preferences-modal.php:31, public/partials/privacy-preferences-modal.php:48
153
+ msgid "Consent Management"
154
+ msgstr ""
155
+
156
+ #: admin/class-gdpr-admin.php:841
157
+ msgid "Profile Updated. These are the user consents after the save:"
158
+ msgstr ""
159
+
160
+ #: admin/class-gdpr-requests-admin.php:30, admin/class-gdpr-requests-admin.php:155, admin/class-gdpr-requests-admin.php:203, admin/class-gdpr-requests-admin.php:240
161
+ msgid "We could not verify the user email or the security token. Please try again."
162
+ msgstr ""
163
+
164
+ #: admin/class-gdpr-requests-admin.php:37, admin/class-gdpr-requests-admin.php:286, admin/class-gdpr-requests-admin.php:331, public/partials/confirmation-screens.php:48
165
+ msgid "User not found."
166
+ msgstr ""
167
+
168
+ #: admin/class-gdpr-requests-admin.php:57
169
+ msgid "User %s is the only admin of the site. It cannot be deleted."
170
+ msgstr ""
171
+
172
+ #: admin/class-gdpr-requests-admin.php:78, admin/class-gdpr-requests-admin.php:117
173
+ msgid "User added to the deletion requests list by admin."
174
+ msgstr ""
175
+
176
+ #: admin/class-gdpr-requests-admin.php:80, admin/class-gdpr-requests-admin.php:119
177
+ msgid "User %s was added to the deletion table."
178
+ msgstr ""
179
+
180
+ #: admin/class-gdpr-requests-admin.php:101
181
+ msgid "User already placed a deletion request."
182
+ msgstr ""
183
+
184
+ #: admin/class-gdpr-requests-admin.php:141, admin/class-gdpr-requests-admin.php:189
185
+ msgid "We could not verify the type of request you want to cancel."
186
+ msgstr ""
187
+
188
+ #. translators: The type of request
189
+ #: admin/class-gdpr-requests-admin.php:149, admin/class-gdpr-requests-admin.php:197
190
+ msgid "Type of request '%s' is not an allowed type."
191
+ msgstr ""
192
+
193
+ #. translators: The type of request i.e 'delete'
194
+ #: admin/class-gdpr-requests-admin.php:164
195
+ msgid "User was removed from the %s request list by admin."
196
+ msgstr ""
197
+
198
+ #: admin/class-gdpr-requests-admin.php:167
199
+ msgid "User %s was removed from this request table."
200
+ msgstr ""
201
+
202
+ #. translators: User email
203
+ #: admin/class-gdpr-requests-admin.php:216
204
+ msgid "User %s request was marked as resolved by admin."
205
+ msgstr ""
206
+
207
+ #: admin/class-gdpr-requests-admin.php:218
208
+ msgid "Request was resolved. User %s has been notified."
209
+ msgstr ""
210
+
211
+ #: admin/class-gdpr-requests-admin.php:251, public/class-gdpr-requests-public.php:44
212
+ msgid "User was removed from the site."
213
+ msgstr ""
214
+
215
+ #: admin/class-gdpr-requests-admin.php:256
216
+ msgid "User %s was deleted from the site."
217
+ msgstr ""
218
+
219
+ #: admin/class-gdpr-requests-admin.php:278, admin/class-gdpr-requests-admin.php:317, public/class-gdpr-requests-public.php:81
220
+ msgid "We could not verify the security token. Please try again."
221
+ msgstr ""
222
+
223
+ #: admin/class-gdpr-requests-admin.php:302
224
+ msgid "Guest"
225
+ msgstr ""
226
+
227
+ #: admin/class-gdpr-requests-admin.php:306
228
+ msgid "User comments were anonymized."
229
+ msgstr ""
230
+
231
+ #: admin/class-gdpr-requests-admin.php:321
232
+ msgid "Essential data missing. Please try again."
233
+ msgstr ""
234
+
235
+ #. translators: 1: The post type, 2: The user the posts were reassigned to
236
+ #: admin/class-gdpr-requests-admin.php:352
237
+ msgid "User %s were reassigned to %s."
238
+ msgstr ""
239
+
240
+ #: admin/class-gdpr-requests-admin.php:356
241
+ msgid "Something went wrong. Please try again."
242
+ msgstr ""
243
+
244
+ #: admin/class-gdpr-telemetry.php:47
245
+ msgid "Telemetry"
246
+ msgstr ""
247
+
248
+ #: admin/class-gdpr-telemetry.php:49
249
+ msgid "No items found. Future connections will be shown at this place."
250
+ msgstr ""
251
+
252
+ #: admin/class-gdpr-telemetry.php:50
253
+ msgid "No items found in trash."
254
+ msgstr ""
255
+
256
+ #: admin/class-gdpr-telemetry.php:51
257
+ msgid "Search in destination"
258
+ msgstr ""
259
+
260
+ #: admin/class-gdpr-telemetry.php:185
261
+ msgid "Delete all"
262
+ msgstr ""
263
+
264
+ #: admin/class-gdpr-telemetry.php:197
265
+ msgid "Destination"
266
+ msgstr ""
267
+
268
+ #: admin/class-gdpr-telemetry.php:198
269
+ msgid "File"
270
+ msgstr ""
271
+
272
+ #: admin/class-gdpr-telemetry.php:199
273
+ msgid "Code"
274
+ msgstr ""
275
+
276
+ #: admin/class-gdpr-telemetry.php:200
277
+ msgid "Time"
278
+ msgstr ""
279
+
280
+ #: admin/class-gdpr-telemetry.php:201
281
+ msgid "Data"
282
+ msgstr ""
283
+
284
+ #: admin/class-gdpr-telemetry.php:343
285
+ msgid "Show"
286
+ msgstr ""
287
+
288
+ #: includes/class-gdpr-email.php:158
289
+ msgid "Data breach notification sent to user."
290
+ msgstr ""
291
+
292
+ #. translators: email content
293
+ #: includes/class-gdpr-email.php:160
294
+ msgid "Email content: %s"
295
+ msgstr ""
296
+
297
+ #. translators: nature of the data breach
298
+ #: includes/class-gdpr-email.php:162
299
+ msgid "Nature of data breach: %s"
300
+ msgstr ""
301
+
302
+ #. translators: data protection officer contact information
303
+ #: includes/class-gdpr-email.php:164
304
+ msgid "Data protection officer contact: %s"
305
+ msgstr ""
306
+
307
+ #. translators: likely consequences
308
+ #: includes/class-gdpr-email.php:166
309
+ msgid "Likely consequences of breach: %s"
310
+ msgstr ""
311
+
312
+ #. translators: measures taken
313
+ #: includes/class-gdpr-email.php:168
314
+ msgid "Measures taken or proposed to be taken: %s"
315
+ msgstr ""
316
+
317
+ #: includes/class-gdpr-email.php:197
318
+ msgid "GDPR Notification: There is a new request waiting to be reviewed."
319
+ msgstr ""
320
+
321
+ #: includes/class-gdpr-email.php:198
322
+ msgid "Someone requested to close your account."
323
+ msgstr ""
324
+
325
+ #: includes/class-gdpr-email.php:199
326
+ msgid "Your account has been closed."
327
+ msgstr ""
328
+
329
+ #: includes/class-gdpr-email.php:200
330
+ msgid "Someone requested that we rectify data of your account."
331
+ msgstr ""
332
+
333
+ #: includes/class-gdpr-email.php:201, includes/class-gdpr-email.php:203, includes/class-gdpr-email.php:205
334
+ msgid "Your request has been completed."
335
+ msgstr ""
336
+
337
+ #: includes/class-gdpr-email.php:202
338
+ msgid "Someone made complaint on behalf of your account."
339
+ msgstr ""
340
+
341
+ #: includes/class-gdpr-email.php:204
342
+ msgid "Someone requested to download your data."
343
+ msgstr ""
344
+
345
+ #: includes/class-gdpr-email.php:206
346
+ msgid "Someone requested to send a data breach notification."
347
+ msgstr ""
348
+
349
+ #: includes/class-gdpr-email.php:207
350
+ msgid "Data Breach Notification."
351
+ msgstr ""
352
+
353
+ #: includes/class-gdpr-help.php:30, includes/class-gdpr-help.php:34, includes/class-gdpr-help.php:73, includes/class-gdpr-help.php:77, includes/class-gdpr-help.php:177, includes/class-gdpr-help.php:184
354
+ msgid "Overview"
355
+ msgstr ""
356
+
357
+ #: includes/class-gdpr-help.php:31
358
+ msgid "This page has multiple request tables. Users can request multiple things like getting deleted from the site or having their data rectified. All requests will come to these tables."
359
+ msgstr ""
360
+
361
+ #: includes/class-gdpr-help.php:39
362
+ msgid "Users may request to have their data rectified. They can place a request somewhere on your site and those requests will show up here."
363
+ msgstr ""
364
+
365
+ #: includes/class-gdpr-help.php:40
366
+ msgid "When you complete the request, mark it as resolved and the requester will get a notification email confirming that their request was resolved."
367
+ msgstr ""
368
+
369
+ #: includes/class-gdpr-help.php:47, includes/class-gdpr-help.php:52, admin/partials/requests.php:89
370
+ msgid "Complaints"
371
+ msgstr ""
372
+
373
+ #: includes/class-gdpr-help.php:48
374
+ msgid "Users may complain about something that happened. They can place a complaint somewhere on your site and those complaints will show up here."
375
+ msgstr ""
376
+
377
+ #: includes/class-gdpr-help.php:49
378
+ msgid "When you resolve the problem, mark it as resolved and the requester will get a notification email confirming that his complaint was resolved."
379
+ msgstr ""
380
+
381
+ #: includes/class-gdpr-help.php:57
382
+ msgid "Users may request to be deleted from the site. If they don't have any content published on the site (including comments) they will be removed from the site automatically. Otherwise, they will show up at this review table where you can reassign or delete their published content and anonymize his comments."
383
+ msgstr ""
384
+
385
+ #: includes/class-gdpr-help.php:58
386
+ msgid "When you are ready to delete the user, they will get a notification that their account has been closed. According to GDPR, you have 30 days to fulfill this request. On some occasions, you can ask to extend this time."
387
+ msgstr ""
388
+
389
+ #: includes/class-gdpr-help.php:61
390
+ msgid "Erasures"
391
+ msgstr ""
392
+
393
+ #: includes/class-gdpr-help.php:74
394
+ msgid "We added tools to make your life easier when you need to perform administrative tasks like notify all your users of a possible data breach."
395
+ msgstr ""
396
+
397
+ #: includes/class-gdpr-help.php:82
398
+ msgid "Use this page to look for all known data about a user. You can look it up using the user's email address and are able to download it in XML and JSON formats."
399
+ msgstr ""
400
+
401
+ #: includes/class-gdpr-help.php:89
402
+ msgid "Data Breach Notification"
403
+ msgstr ""
404
+
405
+ #: includes/class-gdpr-help.php:90
406
+ msgid "Use this carefully."
407
+ msgstr ""
408
+
409
+ #: includes/class-gdpr-help.php:91
410
+ msgid "This will send a mass email to all your users with the information provided on these fields. This email is throttled based on the hourly limit set on the plugin settings page. "
411
+ msgstr ""
412
+
413
+ #: includes/class-gdpr-help.php:92
414
+ msgid "Only use this tool if you believe your site has been compromised and that your user's personal data might have been leaked."
415
+ msgstr ""
416
+
417
+ #: includes/class-gdpr-help.php:100
418
+ msgid "We do not log any of the user's personal data."
419
+ msgstr ""
420
+
421
+ #: includes/class-gdpr-help.php:101
422
+ msgid "All logs are encrypted before saving to the database. An encrypted log file is created whenever a user gets removed from the site."
423
+ msgstr ""
424
+
425
+ #: includes/class-gdpr-help.php:102
426
+ msgid "This tool will keep a record of some actions such as changing consent preferences, placing a request, data breach notifications received, etc…"
427
+ msgstr ""
428
+
429
+ #: includes/class-gdpr-help.php:103
430
+ msgid "The only way to read the logs is to search for the user email. If the data subject is not a registered site user anymore, you need to ask for the 6 digit token that was provided during deletion. That will allow this tool to look for a log file with his information."
431
+ msgstr ""
432
+
433
+ #: includes/class-gdpr-help.php:118, includes/class-gdpr-help.php:123
434
+ msgid "General Settings"
435
+ msgstr ""
436
+
437
+ #: includes/class-gdpr-help.php:119
438
+ msgid "This plugin needs to know your privacy policy page to track updates to it and ask users to re-consent to your new terms."
439
+ msgstr ""
440
+
441
+ #: includes/class-gdpr-help.php:120
442
+ msgid "When sending a data breach notification to your users, we need to throttle the emails because of server limitations. This is an hourly limit. Check with your hosting provider before changing this value."
443
+ msgstr ""
444
+
445
+ #: includes/class-gdpr-help.php:127, includes/class-gdpr-help.php:140
446
+ msgid "Cookie Management"
447
+ msgstr ""
448
+
449
+ #: includes/class-gdpr-help.php:128
450
+ msgid "Fill out every information you can about the cookies your site uses. Set the cookies that you set under Cookies Used and cookies used and set by third parties under the hosts section."
451
+ msgstr ""
452
+
453
+ #: includes/class-gdpr-help.php:130
454
+ msgid "You must ask your developer to wrap the code that sets the cookies with our helper function %s."
455
+ msgstr ""
456
+
457
+ #: includes/class-gdpr-help.php:131
458
+ msgid "Some services like Google Analytics provide a way to opt out from their code with an extra parameter to their snippet."
459
+ msgstr ""
460
+
461
+ #: includes/class-gdpr-help.php:132, includes/class-gdpr-help.php:149
462
+ msgid "External Links"
463
+ msgstr ""
464
+
465
+ #: includes/class-gdpr-help.php:134, includes/class-gdpr-help.php:134
466
+ msgid "WordPress cookies"
467
+ msgstr ""
468
+
469
+ #: includes/class-gdpr-help.php:144
470
+ msgid "Consent Management ( Coming Soon )"
471
+ msgstr ""
472
+
473
+ #: includes/class-gdpr-help.php:145
474
+ msgid "All consents are disabled by default. On first registration, your users will need to consent to your privacy policy. Depending on your privacy policy you should register multiple types of consent on this page and allow them to be toggled on/off."
475
+ msgstr ""
476
+
477
+ #: includes/class-gdpr-help.php:147
478
+ msgid "If you have an optional consent type, you must have a developer wrap the functionality in our helper function %s."
479
+ msgstr ""
480
+
481
+ #: includes/class-gdpr-help.php:148
482
+ msgid "i.e."
483
+ msgstr ""
484
+
485
+ #: includes/class-gdpr-help.php:148
486
+ msgid "You registered email marketing as an optional consent but the user did not actively opt into it on their profile page. You should have your email capture form wrapped in our helper function to block registration or better yet, not even display the email capture form. Same goes for blocking adding the user to your mailing system on registration if consent is not given."
487
+ msgstr ""
488
+
489
+ #: includes/class-gdpr-help.php:151, includes/class-gdpr-help.php:151
490
+ msgid "Article 7 - Conditions for consent"
491
+ msgstr ""
492
+
493
+ #: includes/class-gdpr-help.php:152, includes/class-gdpr-help.php:152
494
+ msgid "Article 8 - conditions applicable to child's consent in relation to information society services"
495
+ msgstr ""
496
+
497
+ #: includes/class-gdpr-help.php:153, includes/class-gdpr-help.php:153
498
+ msgid "Recital 42 - Burden of proof and requirements for consent"
499
+ msgstr ""
500
+
501
+ #: includes/class-gdpr-help.php:154, includes/class-gdpr-help.php:154
502
+ msgid "Recital 43 - Freely Given consent"
503
+ msgstr ""
504
+
505
+ #: includes/class-gdpr-help.php:178
506
+ msgid "This is all data that are being sent outside of your site. WordPress send some data to it's servers to be able to do automatic updates. You can reduce the amount of data being sent using filters."
507
+ msgstr ""
508
+
509
+ #: includes/class-gdpr-help.php:179
510
+ msgid "Some plugins also capture data and send it to their servers. Such practice is not allowed for plugins hosted on wordpress.org plugin repository. In case this is a Premium plugin, you should have been given the option to choose which type of data you want to send."
511
+ msgstr ""
512
+
513
+ #: includes/class-gdpr-help.php:180
514
+ msgid "Use this tool to identify plugins or themes sending potential personal data outside of your server and take action if necessary."
515
+ msgstr ""
516
+
517
+ #: includes/class-gdpr-requests.php:214
518
+ msgid "User request expired. Removing %s user_meta."
519
+ msgstr ""
520
+
521
+ #: includes/class-gdpr.php:299
522
+ msgid "User registered to the site."
523
+ msgstr ""
524
+
525
+ #. translators: Name of consent
526
+ #: includes/class-gdpr.php:306
527
+ msgid "User gave explicit consent to %s"
528
+ msgstr ""
529
+
530
+ #: includes/helper-functions.php:26, public/partials/privacy-bar.php:23
531
+ msgid "Privacy Preferences"
532
+ msgstr ""
533
+
534
+ #: public/class-gdpr-public.php:124
535
+ msgid "Aborting"
536
+ msgstr ""
537
+
538
+ #: public/class-gdpr-public.php:138
539
+ msgid "I Agree"
540
+ msgstr ""
541
+
542
+ #: public/class-gdpr-public.php:193
543
+ msgid "You need to at least consent to our Privacy Policy."
544
+ msgstr ""
545
+
546
+ #: public/class-gdpr-public.php:230
547
+ msgid "User updated their privacy preferences. These are the new approved cookies and consent preferences:"
548
+ msgstr ""
549
+
550
+ #: public/class-gdpr-public.php:305
551
+ msgid "User consented to the Privacy Policies."
552
+ msgstr ""
553
+
554
+ #: public/class-gdpr-requests-public.php:77
555
+ msgid "Invalid type of request. Please try again."
556
+ msgstr ""
557
+
558
+ #: public/class-gdpr-requests-public.php:91, public/class-gdpr-requests-public.php:104
559
+ msgid "Please verify that you are not a robot."
560
+ msgstr ""
561
+
562
+ #: public/class-gdpr-requests-public.php:308
563
+ msgid "User confirmed a request to be deleted."
564
+ msgstr ""
565
+
566
+ #: public/class-gdpr-requests-public.php:310
567
+ msgid "Content was found for that user."
568
+ msgstr ""
569
+
570
+ #: public/class-gdpr-requests-public.php:312
571
+ msgid "User added to the erasure review table."
572
+ msgstr ""
573
+
574
+ #: public/class-gdpr-requests-public.php:346
575
+ msgid "User placed a request for rectification or a complaint."
576
+ msgstr ""
577
+
578
+ #. translators: File format. Can be XML or JSON
579
+ #: public/class-gdpr-requests-public.php:363
580
+ msgid "User downloaded all their data in %s format."
581
+ msgstr ""
582
+
583
+ #: admin/partials/requests.php:36, admin/partials/requests.php:79, admin/partials/requests.php:93, admin/partials/requests.php:136, admin/partials/requests.php:163, admin/partials/requests.php:313
584
+ msgid "Email"
585
+ msgstr ""
586
+
587
+ #: admin/partials/requests.php:37, admin/partials/requests.php:80, admin/partials/requests.php:164, admin/partials/requests.php:314
588
+ msgid "Date of Request"
589
+ msgstr ""
590
+
591
+ #: admin/partials/requests.php:38, admin/partials/requests.php:81, admin/partials/requests.php:95, admin/partials/requests.php:138
592
+ msgid "Information"
593
+ msgstr ""
594
+
595
+ #: admin/partials/requests.php:39, admin/partials/requests.php:82, admin/partials/requests.php:96, admin/partials/requests.php:139, admin/partials/requests.php:166, admin/partials/requests.php:316
596
+ msgid "Actions"
597
+ msgstr ""
598
+
599
+ #: admin/partials/requests.php:56, admin/partials/requests.php:113, admin/partials/requests.php:197
600
+ msgid "Cancel Request"
601
+ msgstr ""
602
+
603
+ #: admin/partials/requests.php:64, admin/partials/requests.php:121
604
+ msgid "Mark as Resolved"
605
+ msgstr ""
606
+
607
+ #: admin/partials/requests.php:72, admin/partials/requests.php:129, admin/partials/requests.php:306
608
+ msgid "No pending requests"
609
+ msgstr ""
610
+
611
+ #: admin/partials/requests.php:94, admin/partials/requests.php:137
612
+ msgid "Date of Complaint"
613
+ msgstr ""
614
+
615
+ #: admin/partials/requests.php:146
616
+ msgid "Right to erasure"
617
+ msgstr ""
618
+
619
+ #: admin/partials/requests.php:153
620
+ msgid "Manually add a user"
621
+ msgstr ""
622
+
623
+ #: admin/partials/requests.php:155, admin/partials/tools.php:63, admin/partials/tools.php:125
624
+ msgid "email@domain.com"
625
+ msgstr ""
626
+
627
+ #: admin/partials/requests.php:156, public/partials/complaint-form.php:11, public/partials/rectify-form.php:10
628
+ msgid "Submit"
629
+ msgstr ""
630
+
631
+ #: admin/partials/requests.php:165, admin/partials/requests.php:180, admin/partials/requests.php:217, admin/partials/requests.php:315
632
+ msgid "Review"
633
+ msgstr ""
634
+
635
+ #: admin/partials/requests.php:182
636
+ msgid "No content to review"
637
+ msgstr ""
638
+
639
+ #: admin/partials/requests.php:204
640
+ msgid "Delete User"
641
+ msgstr ""
642
+
643
+ #: admin/partials/requests.php:215
644
+ msgid "Content Type"
645
+ msgstr ""
646
+
647
+ #: admin/partials/requests.php:216
648
+ msgid "Count"
649
+ msgstr ""
650
+
651
+ #: admin/partials/requests.php:218, admin/partials/requests.php:257
652
+ msgid "Reassign"
653
+ msgstr ""
654
+
655
+ #: admin/partials/requests.php:219
656
+ msgid "Action"
657
+ msgstr ""
658
+
659
+ #: admin/partials/requests.php:259, admin/partials/requests.php:286
660
+ msgid "Resolved"
661
+ msgstr ""
662
+
663
+ #: admin/partials/requests.php:277
664
+ msgid "View Comments"
665
+ msgstr ""
666
+
667
+ #: admin/partials/requests.php:284
668
+ msgid "Anonymize"
669
+ msgstr ""
670
+
671
+ #: admin/partials/settings.php:23
672
+ msgid "Privacy Policy Page"
673
+ msgstr ""
674
+
675
+ #: admin/partials/settings.php:30
676
+ msgid "-- Select --"
677
+ msgstr ""
678
+
679
+ #: admin/partials/settings.php:39
680
+ msgid "Outgoing email limitation"
681
+ msgstr ""
682
+
683
+ #: admin/partials/settings.php:48
684
+ msgid "User deletion"
685
+ msgstr ""
686
+
687
+ #: admin/partials/settings.php:52
688
+ msgid "Send all deletion requests to the review table."
689
+ msgstr ""
690
+
691
+ #: admin/partials/settings.php:57
692
+ msgid "Disable CSS"
693
+ msgstr ""
694
+
695
+ #: admin/partials/settings.php:61
696
+ msgid "Make sure you know what you are doing before checking this."
697
+ msgstr ""
698
+
699
+ #: admin/partials/settings.php:66
700
+ msgid "Enable the Telemetry Tracker"
701
+ msgstr ""
702
+
703
+ #: admin/partials/settings.php:70
704
+ msgid "Toggles the Telemetry Tracker On/Off. (experimental)"
705
+ msgstr ""
706
+
707
+ #: admin/partials/settings.php:75
708
+ msgid "Privacy Center"
709
+ msgstr ""
710
+
711
+ #: admin/partials/settings.php:77
712
+ msgid "This section handles the privacy bar and some of the privacy preferences window."
713
+ msgstr ""
714
+
715
+ #: admin/partials/settings.php:78
716
+ msgid "Important:"
717
+ msgstr ""
718
+
719
+ #: admin/partials/settings.php:78
720
+ msgid "If the privacy banner text is not filled out, the privacy banner will not show up. Even if you registered your cookies."
721
+ msgstr ""
722
+
723
+ #: admin/partials/settings.php:83
724
+ msgid "Privacy Banner Text"
725
+ msgstr ""
726
+
727
+ #: admin/partials/settings.php:92
728
+ msgid "Privacy Excerpt"
729
+ msgstr ""
730
+
731
+ #: admin/partials/settings.php:97
732
+ msgid "This will appear in the consent section of the privacy preference window."
733
+ msgstr ""
734
+
735
+ #: admin/partials/settings.php:102
736
+ msgid "Request Forms reCAPTCHA"
737
+ msgstr ""
738
+
739
+ #: admin/partials/settings.php:103
740
+ msgid "To prevent spam attacks, you have the option to enable reCAPTCHA. Configure below your keys to make it work with our request forms."
741
+ msgstr ""
742
+
743
+ #: admin/partials/settings.php:107
744
+ msgid "You can find the necessary information %s."
745
+ msgstr ""
746
+
747
+ #: admin/partials/settings.php:108
748
+ msgid "here"
749
+ msgstr ""
750
+
751
+ #: admin/partials/settings.php:114
752
+ msgid "Enable reCAPTCHA"
753
+ msgstr ""
754
+
755
+ #: admin/partials/settings.php:123
756
+ msgid "Site Key"
757
+ msgstr ""
758
+
759
+ #: admin/partials/settings.php:132
760
+ msgid "Secret Key"
761
+ msgstr ""
762
+
763
+ #: admin/partials/settings.php:142
764
+ msgid "WooCommerce"
765
+ msgstr ""
766
+
767
+ #: admin/partials/settings.php:147
768
+ msgid "Add consent checkboxes to the registration page"
769
+ msgstr ""
770
+
771
+ #: admin/partials/settings.php:156
772
+ msgid "Add consent checkboxes to the checkout registration form"
773
+ msgstr ""
774
+
775
+ #: admin/partials/settings.php:169
776
+ msgid "Category name"
777
+ msgstr ""
778
+
779
+ #: admin/partials/settings.php:170
780
+ msgid "Add tab"
781
+ msgstr ""
782
+
783
+ #: admin/partials/settings.php:176, admin/partials/templates/tmpl-cookies.php:3
784
+ msgid "Remove this tab."
785
+ msgstr ""
786
+
787
+ #: admin/partials/settings.php:181, admin/partials/templates/tmpl-cookies.php:8
788
+ msgid "Always active"
789
+ msgstr ""
790
+
791
+ #: admin/partials/settings.php:190, admin/partials/templates/tmpl-cookies.php:17
792
+ msgid "How we use"
793
+ msgstr ""
794
+
795
+ #: admin/partials/settings.php:194, admin/partials/templates/tmpl-cookies.php:21
796
+ msgid "Cookies used by the site"
797
+ msgstr ""
798
+
799
+ #: admin/partials/settings.php:198, admin/partials/settings.php:224, admin/partials/templates/tmpl-cookies.php:25, admin/partials/templates/tmpl-cookies.php:57
800
+ msgid "Comma separated list."
801
+ msgstr ""
802
+
803
+ #: admin/partials/settings.php:202
804
+ msgid "Third party domains"
805
+ msgstr ""
806
+
807
+ #: admin/partials/settings.php:205, admin/partials/templates/tmpl-cookies.php:32
808
+ msgid "Add"
809
+ msgstr ""
810
+
811
+ #: admin/partials/settings.php:207
812
+ msgid "Cookies that are set by a third party, like facebook.com."
813
+ msgstr ""
814
+
815
+ #: admin/partials/settings.php:215, admin/partials/templates/tmpl-cookies.php:48
816
+ msgid "Remove this domain."
817
+ msgstr ""
818
+
819
+ #: admin/partials/settings.php:220, admin/partials/templates/tmpl-cookies.php:53
820
+ msgid "Cookies used"
821
+ msgstr ""
822
+
823
+ #: admin/partials/settings.php:228, admin/partials/templates/tmpl-cookies.php:61
824
+ msgid "How to Opt Out"
825
+ msgstr ""
826
+
827
+ #: admin/partials/settings.php:232, admin/partials/templates/tmpl-cookies.php:65
828
+ msgid "Url with instructions on how to opt out."
829
+ msgstr ""
830
+
831
+ #: admin/partials/settings.php:249
832
+ msgid "Type of consent"
833
+ msgstr ""
834
+
835
+ #: admin/partials/settings.php:250
836
+ msgid "Add consent"
837
+ msgstr ""
838
+
839
+ #: admin/partials/settings.php:257, admin/partials/settings.php:258
840
+ msgid "You read and agreed to our %s."
841
+ msgstr ""
842
+
843
+ #: admin/partials/settings.php:265, admin/partials/templates/tmpl-consents.php:3
844
+ msgid "Unregister this consent."
845
+ msgstr ""
846
+
847
+ #: admin/partials/settings.php:270, admin/partials/settings.php:273, public/partials/privacy-preferences-modal.php:57, admin/partials/templates/tmpl-consents.php:9
848
+ msgid "Required"
849
+ msgstr ""
850
+
851
+ #: admin/partials/settings.php:284, admin/partials/templates/tmpl-consents.php:18
852
+ msgid "Consent description"
853
+ msgstr ""
854
+
855
+ #: admin/partials/settings.php:288, admin/partials/templates/tmpl-consents.php:22
856
+ msgid "Registration message"
857
+ msgstr ""
858
+
859
+ #: admin/partials/settings.php:299, admin/partials/settings.php:304
860
+ msgid "Data Export Chanel"
861
+ msgstr ""
862
+
863
+ #: admin/partials/settings.php:310
864
+ msgid "Data Export with email attachment"
865
+ msgstr ""
866
+
867
+ #: admin/partials/settings.php:311
868
+ msgid "Data Export with download link"
869
+ msgstr ""
870
+
871
+ #: admin/partials/tools.php:32
872
+ msgid "Data Breach confirmed. Preparing bulk emails."
873
+ msgstr ""
874
+
875
+ #: admin/partials/tools.php:61, admin/partials/tools.php:123
876
+ msgid "Search by email"
877
+ msgstr ""
878
+
879
+ #: admin/partials/tools.php:64, admin/partials/tools.php:127
880
+ msgid "Search"
881
+ msgstr ""
882
+
883
+ #: admin/partials/tools.php:78
884
+ msgid "Email content"
885
+ msgstr ""
886
+
887
+ #: admin/partials/tools.php:81
888
+ msgid "The content that the end user will see before the below information."
889
+ msgstr ""
890
+
891
+ #: admin/partials/tools.php:85
892
+ msgid "Nature of the personal data breach"
893
+ msgstr ""
894
+
895
+ #: admin/partials/tools.php:88
896
+ msgid "Describe the nature of the personal data breach including where possible, the categories and approximate number of data subjects concerned and the categories and approximate number of personal data records concerned."
897
+ msgstr ""
898
+
899
+ #: admin/partials/tools.php:92
900
+ msgid "Name and contact details of the data protection officer"
901
+ msgstr ""
902
+
903
+ #: admin/partials/tools.php:95
904
+ msgid "Communicate the name and contact details of the data protection officer or other contact point where more information can be obtained."
905
+ msgstr ""
906
+
907
+ #: admin/partials/tools.php:99
908
+ msgid "Likely consequences of the personal data breach"
909
+ msgstr ""
910
+
911
+ #: admin/partials/tools.php:105
912
+ msgid "Measures taken or proposed to be taken"
913
+ msgstr ""
914
+
915
+ #: admin/partials/tools.php:108
916
+ msgid "Describe the measures taken or proposed to be taken by the controller to address the personal data breach, including, where appropriate, measures to mitigate its possible adverse effects."
917
+ msgstr ""
918
+
919
+ #: admin/partials/tools.php:112
920
+ msgid "Send confirmation email"
921
+ msgstr ""
922
+
923
+ #: admin/partials/tools.php:126
924
+ msgid "6 digit token (optional)"
925
+ msgstr ""
926
+
927
+ #: public/partials/confirmation-screens.php:18
928
+ msgid "Close your account?"
929
+ msgstr ""
930
+
931
+ #: public/partials/confirmation-screens.php:23
932
+ msgid "Your account will be closed and all data will be permanently deleted and cannot be recovered. Are you sure?"
933
+ msgstr ""
934
+
935
+ #: public/partials/confirmation-screens.php:27, public/partials/reconsent-modal.php:31
936
+ msgid "Cancel"
937
+ msgstr ""
938
+
939
+ #: public/partials/confirmation-screens.php:34
940
+ msgid "Error!"
941
+ msgstr ""
942
+
943
+ #: public/partials/confirmation-screens.php:36
944
+ msgid "Your account"
945
+ msgstr ""
946
+
947
+ #: public/partials/confirmation-screens.php:38
948
+ msgid "Your account has been closed. We are sorry to see you go."
949
+ msgstr ""
950
+
951
+ #: public/partials/confirmation-screens.php:40
952
+ msgid "Your request has been received and is being reviewed. You will receive an email when we are done."
953
+ msgstr ""
954
+
955
+ #: public/partials/confirmation-screens.php:44
956
+ msgid "Email confirmation"
957
+ msgstr ""
958
+
959
+ #: public/partials/confirmation-screens.php:45
960
+ msgid "We've sent you a confirmation email."
961
+ msgstr ""
962
+
963
+ #: public/partials/confirmation-screens.php:51
964
+ msgid "We can't delete this user."
965
+ msgstr ""
966
+
967
+ #: public/partials/confirmation-screens.php:54
968
+ msgid "Required information is missing from the form."
969
+ msgstr ""
970
+
971
+ #: public/partials/confirmation-screens.php:57
972
+ msgid "Request Received"
973
+ msgstr ""
974
+
975
+ #: public/partials/confirmation-screens.php:58
976
+ msgid "Your request has been received. We will be in touch soon."
977
+ msgstr ""
978
+
979
+ #: public/partials/confirmation-screens.php:61
980
+ msgid "There was a problem with your request. Please try again later."
981
+ msgstr ""
982
+
983
+ #: public/partials/delete-form.php:22
984
+ msgid "Close my account"
985
+ msgstr ""
986
+
987
+ #: public/partials/export-data-form.php:9
988
+ msgid "Download my data"
989
+ msgstr ""
990
+
991
+ #: public/partials/privacy-preferences-modal.php:22
992
+ msgid "Privacy Preference Center"
993
+ msgstr ""
994
+
995
+ #: public/partials/privacy-preferences-modal.php:27
996
+ msgid "Options"
997
+ msgstr ""
998
+
999
+ #: public/partials/privacy-preferences-modal.php:34
1000
+ msgid "Cookie Settings"
1001
+ msgstr ""
1002
+
1003
+ #: public/partials/privacy-preferences-modal.php:84
1004
+ msgid "Cookies Used"
1005
+ msgstr ""
1006
+
1007
+ #: public/partials/privacy-preferences-modal.php:101
1008
+ msgid "Always Active"
1009
+ msgstr ""
1010
+
1011
+ #: public/partials/privacy-preferences-modal.php:120
1012
+ msgid "Opt Out"
1013
+ msgstr ""
1014
+
1015
+ #: public/partials/privacy-preferences-modal.php:135
1016
+ msgid "Save Preferences"
1017
+ msgstr ""
1018
+
1019
+ #: public/partials/privacy-preferences-modal.php:137
1020
+ msgid "More Information"
1021
+ msgstr ""
1022
+
1023
+ #: public/partials/reconsent-modal.php:3
1024
+ msgid "Our Privacy Policy has been updated."
1025
+ msgstr ""
1026
+
1027
+ #: public/partials/reconsent-modal.php:4
1028
+ msgid "To continue using the site you need to read the revised version and agree to the terms."
1029
+ msgstr ""
1030
+
1031
+ #: public/partials/reconsent-modal.php:9
1032
+ msgid "Agree"
1033
+ msgstr ""
1034
+
1035
+ #: public/partials/reconsent-modal.php:10
1036
+ msgid "Disagree"
1037
+ msgstr ""
1038
+
1039
+ #: public/partials/reconsent-modal.php:13
1040
+ msgid "Updating"
1041
+ msgstr ""
1042
+
1043
+ #: public/partials/reconsent-modal.php:22
1044
+ msgid "Are you sure?"
1045
+ msgstr ""
1046
+
1047
+ #: public/partials/reconsent-modal.php:27
1048
+ msgid "By disagreeing you will no longer have access to our site and will be logged out."
1049
+ msgstr ""
1050
+
1051
+ #: public/partials/reconsent-modal.php:30
1052
+ msgid "Continue"
1053
+ msgstr ""
1054
+
1055
+ #: templates/email/complaint-request.php:4
1056
+ msgid ""
1057
+ "Someone placed a complaint on your behalf on our site.\n"
1058
+ "By clicking confirm a request will be made and we will do our best to fulfil it.\n"
1059
+ "\n"
1060
+ "--------------------------------------------------------\n"
1061
+ "Request\n"
1062
+ "--------------------------------------------------------\n"
1063
+ "%s\n"
1064
+ "\n"
1065
+ "\n"
1066
+ "\n"
1067
+ "\n"
1068
+ "To confirm this request, click here: %s\n"
1069
+ "\n"
1070
+ "\n"
1071
+ "\n"
1072
+ "---------------------------------------------------------------------------------\n"
1073
+ "If that wasn't you, reset your password: %s\n"
1074
+ ""
1075
+ msgstr ""
1076
+
1077
+ #: templates/email/complaint-resolved.php:3
1078
+ msgid ""
1079
+ "We resolved your complaint request.\n"
1080
+ "If you have any problems or questions, don't hesitate to contact us."
1081
+ msgstr ""
1082
+
1083
+ #: templates/email/data-breach-notification.php:4
1084
+ msgid ""
1085
+ "%s\n"
1086
+ "\n"
1087
+ "--------------------------------------------------------\n"
1088
+ "Nature of the personal data breach:\n"
1089
+ "--------------------------------------------------------\n"
1090
+ "%s\n"
1091
+ "\n"
1092
+ "--------------------------------------------------------\n"
1093
+ "Name and contact details of the data protection officer:\n"
1094
+ "--------------------------------------------------------\n"
1095
+ "%s\n"
1096
+ "\n"
1097
+ "--------------------------------------------------------\n"
1098
+ "Likely consequences of the personal data breach:\n"
1099
+ "--------------------------------------------------------\n"
1100
+ "%s\n"
1101
+ "\n"
1102
+ "--------------------------------------------------------\n"
1103
+ "Measures taken or proposed to be taken:\n"
1104
+ "--------------------------------------------------------\n"
1105
+ "%s\n"
1106
+ ""
1107
+ msgstr ""
1108
+
1109
+ #: templates/email/data-breach-request.php:4
1110
+ msgid ""
1111
+ "A request to send a mass email notification to all users regarding a data breach has been made by %s.\n"
1112
+ "\n"
1113
+ "--------------------------------------------------------\n"
1114
+ "Nature of the personal data breach:\n"
1115
+ "--------------------------------------------------------\n"
1116
+ "%s\n"
1117
+ "\n"
1118
+ "--------------------------------------------------------\n"
1119
+ "Name and contact details of the data protection officer:\n"
1120
+ "--------------------------------------------------------\n"
1121
+ "%s\n"
1122
+ "\n"
1123
+ "--------------------------------------------------------\n"
1124
+ "Likely consequences of the personal data breach:\n"
1125
+ "--------------------------------------------------------\n"
1126
+ "%s\n"
1127
+ "\n"
1128
+ "--------------------------------------------------------\n"
1129
+ "Measures taken or proposed to be taken:\n"
1130
+ "--------------------------------------------------------\n"
1131
+ "%s\n"
1132
+ "\n"
1133
+ "\n"
1134
+ "To confirm this request, click here: %s\n"
1135
+ "\n"
1136
+ "---------------------------------------------------------------------------------\n"
1137
+ "If that is not intended, have the person who requested it change their password.\n"
1138
+ "---------------------------------------------------------------------------------\n"
1139
+ ""
1140
+ msgstr ""
1141
+
1142
+ #: templates/email/delete-request.php:4
1143
+ msgid ""
1144
+ "Someone placed a request for your information to be removed from our site.\n"
1145
+ "By clicking confirm your account will be removed from our site and all data we collected\n"
1146
+ "over time will be erased from our database. It will be impossible for us to retrieve that\n"
1147
+ "information in the future.\n"
1148
+ "\n"
1149
+ "\n"
1150
+ "\n"
1151
+ "To confirm this request, click here: %s\n"
1152
+ "\n"
1153
+ "\n"
1154
+ "\n"
1155
+ "---------------------------------------------------------------------------------\n"
1156
+ "If that wasn't you, reset your password: %s\n"
1157
+ ""
1158
+ msgstr ""
1159
+
1160
+ #: templates/email/delete-resolved.php:5
1161
+ msgid ""
1162
+ "Your account has been closed.\n"
1163
+ "\n"
1164
+ "We no longer hold any information about you.\n"
1165
+ "If you ever need to make a complaint you can email us and we will try to help you.\n"
1166
+ "To be able to make a complaint you will be requested to provide your email address and the token below.\n"
1167
+ "\n"
1168
+ "%s"
1169
+ msgstr ""
1170
+
1171
+ #: templates/email/export-data-request.php:4
1172
+ msgid ""
1173
+ "Someone requested to download your data from our site.\n"
1174
+ "By clicking confirm we will redirect you back to our site where a download will begin.\n"
1175
+ "\n"
1176
+ "To download it in a XML format, click here: %s\n"
1177
+ "To download it in a JSON format, click here: %s\n"
1178
+ "\n"
1179
+ "\n"
1180
+ "\n"
1181
+ "---------------------------------------------------------------------------------\n"
1182
+ "If that wasn't you, reset your password: %s\n"
1183
+ ""
1184
+ msgstr ""
1185
+
1186
+ #: templates/email/new-request.php:5
1187
+ msgid ""
1188
+ "There is a new %1$s request waiting for review.\n"
1189
+ "\n"
1190
+ "Review your requests: %2$s"
1191
+ msgstr ""
1192
+
1193
+ #: templates/email/rectify-request.php:4
1194
+ msgid ""
1195
+ "Someone placed a request for your information to be rectified on our site.\n"
1196
+ "By clicking confirm a request will be made and we will do our best to fulfil it.\n"
1197
+ "\n"
1198
+ "--------------------------------------------------------\n"
1199
+ "Request\n"
1200
+ "--------------------------------------------------------\n"
1201
+ "%s\n"
1202
+ "\n"
1203
+ "\n"
1204
+ "\n"
1205
+ "\n"
1206
+ "To confirm this request, click here: %s\n"
1207
+ "\n"
1208
+ "\n"
1209
+ "\n"
1210
+ "---------------------------------------------------------------------------------\n"
1211
+ "If that wasn't you, reset your password: %s\n"
1212
+ ""
1213
+ msgstr ""
1214
+
1215
+ #: templates/email/rectify-resolved.php:3
1216
+ msgid ""
1217
+ "We resolved your rectification request.\n"
1218
+ "If you have any problems or questions, don't hesitate to contact us."
1219
+ msgstr ""
1220
+
1221
+ #: admin/partials/templates/tmpl-cookies.php:29
1222
+ msgid "Third Party Domains"
1223
+ msgstr ""
1224
+
1225
+ #: admin/partials/templates/tmpl-cookies.php:34
1226
+ msgid "Cookies that are set by a third party, like facebook.com"
1227
+ msgstr ""
1228
+
1229
+ #: admin/partials/templates/tmpl-tools.php:3, admin/partials/templates/tmpl-tools.php:23
1230
+ msgid "Result"
1231
+ msgstr ""
1232
+
1233
+ #: admin/partials/templates/tmpl-tools.php:14, admin/partials/templates/tmpl-tools.php:44
1234
+ msgid "Error"
1235
+ msgstr ""
1236
+
1237
+ #: admin/partials/templates/tmpl-tools.php:16
1238
+ msgid "We could not find a any logs for that email and token combination."
1239
+ msgstr ""
1240
+
1241
+ #: admin/partials/templates/tmpl-tools.php:46
1242
+ msgid "We could not find a user with that email."
1243
+ msgstr ""
public/class-gdpr-public.php ADDED
@@ -0,0 +1,359 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * The public-facing functionality of the plugin.
5
+ *
6
+ * @link https://trewknowledge.com
7
+ * @since 1.0.0
8
+ *
9
+ * @package GDPR
10
+ * @subpackage public
11
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
12
+ */
13
+
14
+ /**
15
+ * The public-facing functionality of the plugin.
16
+ *
17
+ * Defines the plugin name and version.
18
+ * Enqueue the admin-specific stylesheet and JavaScript.
19
+ *
20
+ * @package GDPR
21
+ * @subpackage public
22
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
23
+ */
24
+ class GDPR_Public {
25
+
26
+ /**
27
+ * The ID of this plugin.
28
+ *
29
+ * @since 1.0.0
30
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
31
+ * @access private
32
+ * @var string $plugin_name The ID of this plugin.
33
+ */
34
+ private $plugin_name;
35
+
36
+ /**
37
+ * The version of this plugin.
38
+ *
39
+ * @since 1.0.0
40
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
41
+ * @access private
42
+ * @var string $version The current version of this plugin.
43
+ */
44
+ private $version;
45
+
46
+ /**
47
+ * Allowed HTML for wp_kses.
48
+ * @since 1.1.0
49
+ * @access private
50
+ * @var array $allowed_html The allowed HTML for wp_kses.
51
+ */
52
+ private $allowed_html;
53
+
54
+ /**
55
+ * Initialize the class and set its properties.
56
+ *
57
+ * @since 1.0.0
58
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
59
+ * @param string $plugin_name The name of the plugin.
60
+ * @param string $version The version of this plugin.
61
+ */
62
+ public function __construct( $plugin_name, $version ) {
63
+ $this->plugin_name = $plugin_name;
64
+ $this->version = $version;
65
+ $this->allowed_html = array(
66
+ 'a' => array(
67
+ 'href' => true,
68
+ 'title' => true,
69
+ 'target' => true,
70
+ ),
71
+ );
72
+ }
73
+
74
+ /**
75
+ * Checks if recaptcha is being used and add the code.
76
+ * Should be called from the request forms.
77
+ * @since 1.4.0
78
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
79
+ */
80
+ public static function add_recaptcha() {
81
+ $use_recaptcha = get_option( 'gdpr_use_recaptcha', false );
82
+ if ( $use_recaptcha ) {
83
+ $site_key = get_option( 'gdpr_recaptcha_site_key', '' );
84
+ $secret_key = get_option( 'gdpr_recaptcha_secret_key', '' );
85
+
86
+ if ( $site_key && $secret_key ) {
87
+ echo '<div class="g-recaptcha" data-sitekey="' . $site_key . '"></div>';
88
+ }
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Register the stylesheets for the public-facing side of the site.
94
+ *
95
+ * @since 1.0.0
96
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
97
+ */
98
+ public function enqueue_styles() {
99
+ $disable_css = get_option( 'gdpr_disable_css', false );
100
+ if ( ! $disable_css ) {
101
+ wp_enqueue_style( $this->plugin_name, plugin_dir_url( dirname( __FILE__ ) ) . 'assets/css/gdpr-public.css', array(), $this->version, 'all' );
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Register the JavaScript for the public-facing side of the site.
107
+ *
108
+ * @since 1.0.0
109
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
110
+ */
111
+ public function enqueue_scripts() {
112
+ $use_recaptcha = get_option( 'gdpr_use_recaptcha', false );
113
+ if ( $use_recaptcha ) {
114
+ $site_key = get_option( 'gdpr_recaptcha_site_key', '' );
115
+ $secret_key = get_option( 'gdpr_recaptcha_secret_key', '' );
116
+
117
+ if ( $site_key && $secret_key ) {
118
+ wp_enqueue_script( $this->plugin_name . '-recaptcha', 'https://www.google.com/recaptcha/api.js' );
119
+ }
120
+ }
121
+ wp_enqueue_script( $this->plugin_name, plugin_dir_url( dirname( __FILE__ ) ) . 'assets/js/gdpr-public.js', array( 'jquery' ), $this->version, false );
122
+ wp_localize_script( $this->plugin_name, 'GDPR', array(
123
+ 'ajaxurl' => admin_url( 'admin-ajax.php' ),
124
+ 'aborting' => esc_html__( 'Aborting', 'gdpr' ),
125
+ 'is_user_logged_in' => is_user_logged_in(),
126
+ 'privacy_page_id' => get_option( 'gdpr_privacy_policy_page', 0 ),
127
+ ) );
128
+ }
129
+
130
+ /**
131
+ * Prints the privacy bar for the end user to save the consent and cookie settings.
132
+ * @since 1.0.0
133
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
134
+ */
135
+ public function privacy_bar() {
136
+ $content = get_option( 'gdpr_cookie_banner_content', '' );
137
+ $tabs = get_option( 'gdpr_cookie_popup_content', array() );
138
+ $button_text = apply_filters( 'gdpr_privacy_bar_button_text', esc_html__( 'I Agree', 'gdpr' ) );
139
+
140
+ if ( empty( $content ) ) {
141
+ return;
142
+ }
143
+
144
+ include plugin_dir_path( __FILE__ ) . 'partials/privacy-bar.php';
145
+ }
146
+
147
+ /**
148
+ * The privacy preferences modal.
149
+ * @since 1.0.0
150
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
151
+ */
152
+ public function privacy_preferences_modal() {
153
+ $cookie_privacy_excerpt = get_option( 'gdpr_cookie_privacy_excerpt', '' );
154
+ $consent_types = get_option( 'gdpr_consent_types', array() );
155
+ $privacy_policy_page = get_option( 'gdpr_privacy_policy_page', 0 );
156
+ $approved_cookies = isset( $_COOKIE['gdpr']['allowed_cookies'] ) ? json_decode( wp_unslash( $_COOKIE['gdpr']['allowed_cookies'] ) ) : array();
157
+ $user_consents = isset( $_COOKIE['gdpr']['consent_types'] ) ? json_decode( wp_unslash( $_COOKIE['gdpr']['consent_types'] ) ) : array();
158
+ $tabs = get_option( 'gdpr_cookie_popup_content', array() );
159
+
160
+ include plugin_dir_path( __FILE__ ) . 'partials/privacy-preferences-modal.php';
161
+ }
162
+
163
+ /**
164
+ * The black overlay for the plugin modals.
165
+ * @since 1.0.0
166
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
167
+ */
168
+ public function overlay() {
169
+ echo '<div class="gdpr-overlay"></div>';
170
+ }
171
+
172
+ /**
173
+ * Prints the confirmation dialogs.
174
+ * @since 1.0.0
175
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
176
+ */
177
+ public function confirmation_screens() {
178
+ require_once plugin_dir_path( dirname( __FILE__ ) ) . 'public/partials/confirmation-screens.php';
179
+ }
180
+
181
+ /**
182
+ * Update the user allowed cookies and types of consent.
183
+ * If the user is logged in, we also save consent to user meta.
184
+ * @since 1.1.0
185
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
186
+ */
187
+ public function update_privacy_preferences() {
188
+ if ( ! isset( $_POST['update-privacy-preferences-nonce'] ) || ! wp_verify_nonce( sanitize_key( $_POST['update-privacy-preferences-nonce'] ), 'gdpr-update_privacy_preferences' ) ) {
189
+ wp_die( esc_html__( 'We could not verify the the security token. Please try again.', 'gdpr' ) );
190
+ }
191
+
192
+ if ( ! isset( $_POST['user_consents'] ) ) {
193
+ wp_die( esc_html__( "You need to at least consent to our Privacy Policy.", 'gdpr' ) );
194
+ }
195
+
196
+ $consents = array_map( 'sanitize_text_field', (array) $_POST['user_consents'] );
197
+ $cookies = isset( $_POST['approved_cookies'] ) ? array_map( 'sanitize_text_field', (array) $_POST['approved_cookies'] ) : array();
198
+ $all_cookies = isset( $_POST['all_cookies'] ) ? array_map( 'sanitize_text_field', (array) json_decode( wp_unslash( $_POST['all_cookies'] ) ) ) : array();
199
+
200
+ $approved_cookies = array();
201
+ if ( ! empty( $cookies ) ) {
202
+ foreach ( $cookies as $cookieArr ) {
203
+ $cookieArr = json_decode( wp_unslash( $cookieArr ) );
204
+ foreach ( $cookieArr as $cookie ) {
205
+ $approved_cookies[] = $cookie;
206
+ }
207
+ }
208
+ }
209
+
210
+ $cookies_to_remove = array_diff( $all_cookies, $approved_cookies );
211
+
212
+ $cookies_as_json = json_encode( $approved_cookies );
213
+ $consents_as_json = json_encode( $consents );
214
+
215
+ setcookie( "gdpr[allowed_cookies]", $cookies_as_json, time() + YEAR_IN_SECONDS, "/" );
216
+ setcookie( "gdpr[consent_types]", $consents_as_json, time() + YEAR_IN_SECONDS, "/" );
217
+
218
+ foreach ( $cookies_to_remove as $cookie ) {
219
+ if ( GDPR::similar_in_array( $cookie, array_keys( $_COOKIE ) ) ) {
220
+ $domain = get_site_url();
221
+ $domain = wp_parse_url( $domain, PHP_URL_HOST );
222
+ unset( $_COOKIE[ $cookie ] );
223
+ setcookie( $cookie, NULL, -1, "/", $domain );
224
+ setcookie( $cookie, NULL, -1, "/", '.' . $domain );
225
+ }
226
+ }
227
+
228
+ if ( is_user_logged_in() ) {
229
+ $user = wp_get_current_user();
230
+ GDPR_Audit_Log::log( $user->ID, esc_html__( 'User updated their privacy preferences. These are the new approved cookies and consent preferences:', 'gdpr' ) );
231
+ if ( ! empty( $consents ) ) {
232
+ delete_user_meta( $user->ID, 'gdpr_consents' );
233
+ foreach ( $consents as $consent ) {
234
+ $consent = sanitize_text_field( wp_unslash( $consent ) );
235
+ add_user_meta( $user->ID, 'gdpr_consents', $consent );
236
+ GDPR_Audit_Log::log( $user->ID, 'Consent: ' . $consent );
237
+ }
238
+ }
239
+
240
+ if ( ! empty( $approved_cookies ) ) {
241
+ foreach ( $approved_cookies as $cookie ) {
242
+ GDPR_Audit_Log::log( $user->ID, 'Cookie: ' . $cookie );
243
+ }
244
+ }
245
+
246
+ }
247
+
248
+ wp_safe_redirect( esc_url_raw( wp_get_referer() ) );
249
+ exit;
250
+ }
251
+
252
+ /**
253
+ * Check if the user did not consent to the privacy policy
254
+ * @since 1.0.0
255
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
256
+ * @return bool Whether the user consented or not.
257
+ */
258
+ public function is_consent_needed() {
259
+ $privacy_policy_page = get_option( 'gdpr_privacy_policy_page' );
260
+ if ( ! $privacy_policy_page || ! is_user_logged_in() ) {
261
+ return;
262
+ }
263
+
264
+ $page_obj = get_post( $privacy_policy_page );
265
+ $user = wp_get_current_user();
266
+ $user_consents = get_user_meta( $user->ID, 'gdpr_consents' );
267
+
268
+ if ( in_array( 'privacy-policy', $user_consents ) ) {
269
+ return;
270
+ }
271
+
272
+ include plugin_dir_path( __FILE__ ) . 'partials/reconsent-modal.php';
273
+ }
274
+
275
+ /**
276
+ * Log the user out if they does not agree with the privacy policy terms when prompted.
277
+ * @since 1.0.0
278
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
279
+ */
280
+ public function logout() {
281
+ if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'gdpr-user_disagree_with_terms' ) ) {
282
+ wp_send_json_error( esc_html__( 'We could not verify the the security token. Please try again.', 'gdpr' ) );
283
+ }
284
+
285
+ wp_logout();
286
+ wp_send_json_success();
287
+ }
288
+
289
+ /**
290
+ * The user agreed with the privacy policy terms when prompted.
291
+ * @since 1.0.0
292
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
293
+ */
294
+ public function agree_with_terms() {
295
+ if ( ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['nonce'] ) ), 'gdpr-user_agree_with_terms' ) ) {
296
+ wp_send_json_error( esc_html__( 'We could not verify the the security token. Please try again.', 'gdpr' ) );
297
+ }
298
+
299
+ $user = wp_get_current_user();
300
+ $user_consents = get_user_meta( $user->ID, 'gdpr_consents' );
301
+ $user_consents[] = 'privacy-policy';
302
+ $user_consents = array_unique( $user_consents );
303
+ add_user_meta( $user->ID, 'gdpr_consents', 'privacy-policy' );
304
+ setcookie( "gdpr[consent_types]", json_encode( $user_consents ), time() + YEAR_IN_SECONDS, "/" );
305
+ GDPR_Audit_Log::log( $user->ID, esc_html__( 'User consented to the Privacy Policies.', 'gdpr' ) );
306
+ wp_send_json_success();
307
+ }
308
+
309
+ public function set_plugin_cookies() {
310
+ $user_id = get_current_user_id();
311
+
312
+ if ( ! isset( $_COOKIE['gdpr']['consent_types'] ) ) {
313
+ if ( ! $user_id ) {
314
+ setcookie( 'gdpr[consent_types]', '[]', time() + YEAR_IN_SECONDS, "/" );
315
+ } else {
316
+ $user_consents = get_user_meta( $user_id, 'gdpr_consents' );
317
+ setcookie( "gdpr[consent_types]", json_encode( $user_consents ), time() + YEAR_IN_SECONDS, "/" );
318
+ }
319
+ } else {
320
+ if ( $user_id ) {
321
+ $user_consents = (array) get_user_meta( $user_id, 'gdpr_consents' );
322
+ $cookie_consents = (array) json_decode( wp_unslash( $_COOKIE['gdpr']['consent_types'] ) );
323
+
324
+ $intersect = array_intersect( $user_consents, $cookie_consents );
325
+ $diff = array_merge( array_diff( $user_consents, $intersect ), array_diff( $cookie_consents, $intersect ) );
326
+
327
+ if ( ! empty( $diff ) ) {
328
+ setcookie( "gdpr[consent_types]", json_encode( $user_consents ), time() + YEAR_IN_SECONDS, "/" );
329
+ }
330
+ }
331
+ }
332
+
333
+
334
+ if ( ! isset( $_COOKIE['gdpr']['allowed_cookies'] ) ) {
335
+ $registered_cookies = get_option( 'gdpr_cookie_popup_content', array() );
336
+ $cookies = array();
337
+ if ( ! empty( $registered_cookies ) ) {
338
+ $required_cookies = array_filter( $registered_cookies, function( $item ) {
339
+ return $item['always_active'] == 1;
340
+ });
341
+ if ( ! empty( $required_cookies ) ) {
342
+ foreach ( $required_cookies as $category ) {
343
+ $cookies_used = explode( ',', $category['cookies_used'] );
344
+ foreach ( $cookies_used as $cookie ) {
345
+ $cookies[] = trim( $cookie );
346
+ }
347
+ }
348
+ }
349
+ }
350
+
351
+ if ( ! empty( $cookies ) ) {
352
+ setcookie( "gdpr[allowed_cookies]", json_encode( $cookies ), time() + YEAR_IN_SECONDS, "/" );
353
+ } else {
354
+ setcookie( "gdpr[allowed_cookies]", '[]', time() + YEAR_IN_SECONDS, "/" );
355
+ }
356
+ }
357
+ }
358
+
359
+ }
public/class-gdpr-requests-public.php ADDED
@@ -0,0 +1,392 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * The public facing requests functionality of the plugin.
5
+ *
6
+ * @link https://trewknowledge.com
7
+ * @since 1.0.0
8
+ *
9
+ * @package GDPR
10
+ * @subpackage admin
11
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
12
+ */
13
+
14
+ /**
15
+ * The public facing requests functionality of the plugin.
16
+ *
17
+ * @package GDPR
18
+ * @subpackage admin
19
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
20
+ */
21
+ class GDPR_Requests_Public extends GDPR_Requests {
22
+
23
+ /**
24
+ * Removes the user from the requests table, sends a notification email and
25
+ * delete the user from the site
26
+ * @since 1.0.0
27
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
28
+ * @param WP_User $user The user object.
29
+ * @param string $index The request key on the requests array.
30
+ * @return void
31
+ */
32
+ public function delete_user( $user, $index ) {
33
+ if ( ! $user instanceof WP_User ) {
34
+ return false;
35
+ }
36
+
37
+ if ( ! function_exists( 'wp_delete_user' ) ) {
38
+ require_once( ABSPATH . 'wp-admin/includes/user.php' );
39
+ }
40
+
41
+ if ( parent::remove_from_requests( $index ) ) {
42
+ $token = GDPR::generate_pin();
43
+ GDPR_Email::send( $user->user_email, 'delete-resolved', array( 'token' => $token ) );
44
+ GDPR_Audit_Log::log( $user->ID, esc_html__( 'User was removed from the site.', 'gdpr' ) );
45
+ GDPR_Audit_Log::export_log( $user->ID, $token );
46
+ wp_delete_user( $user->ID );
47
+ wp_logout();
48
+ return true;
49
+ }
50
+ }
51
+
52
+ /**
53
+ * Prints a request form.
54
+ * @since 1.0.0
55
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
56
+ * @static
57
+ * @param string $type The type of request to display the correct form.
58
+ * @return mixed Print the form html.
59
+ */
60
+ public static function request_form( $type ) {
61
+ if ( ! in_array( $type, parent::$allowed_types ) ) {
62
+ return;
63
+ }
64
+
65
+ ob_start();
66
+ include plugin_dir_path( dirname( __FILE__ ) ) . 'public/partials/' . $type . '-form.php';
67
+ return ob_get_clean();
68
+ }
69
+
70
+ /**
71
+ * Sends an email to the end user so it can confirm his request.
72
+ * @since 1.0.0
73
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
74
+ */
75
+ public function send_request_email() {
76
+ if ( ! isset( $_POST['type'] ) || ! in_array( $_POST['type'], parent::$allowed_types ) ) {
77
+ wp_die( esc_html__( 'Invalid type of request. Please try again.', 'gdpr' ) );
78
+ }
79
+
80
+ if ( ! isset( $_POST['gdpr_request_nonce'] ) || ! wp_verify_nonce( sanitize_key( $_POST['gdpr_request_nonce'] ), 'gdpr-add-to-requests' ) ) {
81
+ wp_die( esc_html__( 'We could not verify the security token. Please try again.', 'gdpr' ) );
82
+ }
83
+
84
+ $use_recaptcha = get_option( 'gdpr_use_recaptcha', false );
85
+ if ( $use_recaptcha ) {
86
+ $site_key = get_option( 'gdpr_recaptcha_site_key', '' );
87
+ $secret_key = get_option( 'gdpr_recaptcha_secret_key', '' );
88
+
89
+ if ( $site_key && $secret_key ) {
90
+ if ( ! isset( $_POST['g-recaptcha-response'] ) || ! $_POST['g-recaptcha-response'] ) {
91
+ wp_die( esc_html__( 'Please verify that you are not a robot.', 'gdpr' ) );
92
+ }
93
+
94
+ $response = wp_remote_post( 'https://www.google.com/recaptcha/api/siteverify', array(
95
+ 'body' => array(
96
+ 'secret' => $secret_key,
97
+ 'response' => $_POST['g-recaptcha-response'],
98
+ ),
99
+ ) );
100
+
101
+ $recaptcha_result = wp_remote_retrieve_body( $response );
102
+ $recaptcha_result = json_decode( $recaptcha_result );
103
+ if ( ! $recaptcha_result || ! $recaptcha_result->success ) {
104
+ wp_die( esc_html__( 'Please verify that you are not a robot.', 'gdpr' ) );
105
+ }
106
+
107
+ }
108
+ }
109
+
110
+ $type = sanitize_text_field( wp_unslash( $_POST['type'] ) );
111
+ $data = isset( $_POST['data'] ) ? sanitize_textarea_field( $_POST['data'] ) : '';
112
+
113
+ if ( is_user_logged_in() ) {
114
+ $user = wp_get_current_user();
115
+ } else {
116
+ $user = isset( $_POST['user_email'] ) ? get_user_by( 'email', sanitize_email( $_POST['user_email'] ) ) : null;
117
+ }
118
+
119
+ $email_args = array(
120
+ 'forgot_password_url' => add_query_arg(
121
+ array(
122
+ 'action' => 'rp',
123
+ 'key' => get_password_reset_key( $user ),
124
+ 'login' => $user->user_login,
125
+ ),
126
+ wp_login_url()
127
+ ),
128
+ );
129
+
130
+ switch ( $type ) {
131
+ case 'delete':
132
+ if ( ! $user instanceof WP_User ) {
133
+ wp_safe_redirect(
134
+ esc_url_raw(
135
+ add_query_arg(
136
+ array(
137
+ 'notify' => 1,
138
+ 'user-not-found' => 1,
139
+ ),
140
+ wp_get_referer()
141
+ )
142
+ )
143
+ );
144
+ exit;
145
+ }
146
+
147
+ if ( in_array( 'administrator', $user->roles ) ) {
148
+ $admins_query = new WP_User_Query( array(
149
+ 'role' => 'Administrator',
150
+ ) );
151
+ if ( 1 === $admins_query->get_total() ) {
152
+ wp_safe_redirect(
153
+ esc_url_raw(
154
+ add_query_arg(
155
+ array(
156
+ 'notify' => 1,
157
+ 'cannot-delete' => 1,
158
+ ),
159
+ wp_get_referer()
160
+ )
161
+ )
162
+ );
163
+ exit;
164
+ }
165
+ }
166
+ break;
167
+
168
+ case 'rectify':
169
+ case 'complaint':
170
+ if ( ! $data ) {
171
+ wp_safe_redirect(
172
+ esc_url_raw(
173
+ add_query_arg(
174
+ array(
175
+ 'notify' => 1,
176
+ 'required-information-missing' => 1,
177
+ ),
178
+ wp_get_referer()
179
+ )
180
+ )
181
+ );
182
+ exit;
183
+ }
184
+ $email_args['data'] = $data;
185
+ break;
186
+ case 'export-data':
187
+ if ( ! $user instanceof WP_User ) {
188
+ wp_safe_redirect(
189
+ esc_url_raw(
190
+ add_query_arg(
191
+ array(
192
+ 'notify' => 1,
193
+ 'user-not-found' => 1,
194
+ ),
195
+ wp_get_referer()
196
+ )
197
+ )
198
+ );
199
+ exit;
200
+ }
201
+ break;
202
+ }
203
+
204
+ $key = parent::add_to_requests( $user->user_email, $type, $data );
205
+
206
+ if ( 'export-data' !== $type ) {
207
+ $email_args['confirm_url'] = add_query_arg(
208
+ array(
209
+ 'type' => $type,
210
+ 'key' => $key,
211
+ 'email' => $user->user_email,
212
+ ),
213
+ home_url()
214
+ );
215
+ } else {
216
+ $email_args['confirm_url_xml'] = add_query_arg(
217
+ array(
218
+ 'type' => $type,
219
+ 'key' => $key,
220
+ 'email' => $user->user_email,
221
+ 'format' => 'xml',
222
+ ),
223
+ home_url()
224
+ );
225
+ $email_args['confirm_url_json'] = add_query_arg(
226
+ array(
227
+ 'type' => $type,
228
+ 'key' => $key,
229
+ 'email' => $user->user_email,
230
+ 'format' => 'json',
231
+ ),
232
+ home_url()
233
+ );
234
+ }
235
+
236
+
237
+ if ( GDPR_Email::send(
238
+ $user->user_email,
239
+ "{$type}-request",
240
+ $email_args
241
+ ) ) {
242
+ wp_safe_redirect(
243
+ esc_url_raw(
244
+ add_query_arg(
245
+ array(
246
+ 'notify' => 1,
247
+ 'email-sent' => 1,
248
+ ),
249
+ wp_get_referer()
250
+ )
251
+ )
252
+ );
253
+ exit;
254
+ } else {
255
+ wp_safe_redirect(
256
+ esc_url_raw(
257
+ add_query_arg(
258
+ array(
259
+ 'notify' => 1,
260
+ 'error' => 1,
261
+ ),
262
+ wp_get_referer()
263
+ )
264
+ )
265
+ );
266
+ exit;
267
+ }
268
+ }
269
+
270
+ /**
271
+ * Runs when a user confirms a request email.
272
+ * This process the request, set the request to confirmed on the database.
273
+ * @since 1.0.0
274
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
275
+ */
276
+ public function request_confirmed() {
277
+ if ( ! is_front_page() || ! isset( $_GET['type'], $_GET['key'], $_GET['email'] ) ) {
278
+ return;
279
+ }
280
+
281
+ $type = sanitize_text_field( wp_unslash( $_GET['type'] ) );
282
+ $key = sanitize_text_field( wp_unslash( $_GET['key'] ) );
283
+ $email = sanitize_email( $_GET['email'] );
284
+ $notification_email = sanitize_email( apply_filters( 'gdpr_admin_notification_email', get_option( 'admin_email' ) ) );
285
+
286
+ $user = get_user_by( 'email', $email );
287
+ if ( ! $user instanceof WP_User ) {
288
+ return;
289
+ }
290
+
291
+ $meta_key = get_user_meta( $user->ID, self::$plugin_name . "_{$type}_key", true );
292
+ if ( empty( $meta_key ) ) {
293
+ return;
294
+ }
295
+
296
+ if ( $key === $meta_key ) {
297
+ $notification_email_args = array(
298
+ 'type' => $type,
299
+ 'review_url' => add_query_arg( array( 'page' => 'gdpr-requests#' . $type ), admin_url() ),
300
+ );
301
+ switch ( $type ) {
302
+ case 'delete':
303
+ $found_posts = parent::user_has_content( $user );
304
+ $needs_review = get_option( 'gdpr_deletion_needs_review', true );
305
+ if ( $found_posts || $needs_review ) {
306
+ parent::confirm_request( $key );
307
+ GDPR_Email::send( $notification_email, 'new-request', $notification_email_args );
308
+ GDPR_Audit_Log::log( $user->ID, esc_html__( 'User confirmed a request to be deleted.', 'gdpr' ) );
309
+ if ( $found_posts ) {
310
+ GDPR_Audit_Log::log( $user->ID, esc_html__( 'Content was found for that user.', 'gdpr' ) );
311
+ }
312
+ GDPR_Audit_Log::log( $user->ID, esc_html__( 'User added to the erasure review table.', 'gdpr' ) );
313
+ wp_safe_redirect(
314
+ esc_url_raw(
315
+ add_query_arg(
316
+ array(
317
+ 'user-deleted' => 0,
318
+ 'notify' => 1,
319
+ ),
320
+ home_url()
321
+ )
322
+ )
323
+ );
324
+ exit;
325
+ } else {
326
+ if ( $this->delete_user( $user, $key ) ) {
327
+ wp_safe_redirect(
328
+ esc_url_raw(
329
+ add_query_arg(
330
+ array(
331
+ 'user-deleted' => 1,
332
+ 'notify' => 1,
333
+ ),
334
+ home_url()
335
+ )
336
+ )
337
+ );
338
+ exit;
339
+ }
340
+ }
341
+ break;
342
+ case 'rectify':
343
+ case 'complaint':
344
+ parent::confirm_request( $key );
345
+ GDPR_Email::send( $notification_email, 'new-request', $notification_email_args );
346
+ GDPR_Audit_Log::log( $user->ID, esc_html__( 'User placed a request for rectification or a complaint.', 'gdpr' ) );
347
+ wp_safe_redirect(
348
+ esc_url_raw(
349
+ add_query_arg(
350
+ array(
351
+ 'request-confirmed' => 1,
352
+ 'notify' => 1,
353
+ ),
354
+ home_url()
355
+ )
356
+ )
357
+ );
358
+ exit;
359
+ break;
360
+ case 'export-data':
361
+ $format = isset( $_GET['format'] ) ? sanitize_text_field( wp_unslash( $_GET['format'] ) ) : 'xml';
362
+ /* translators: File format. Can be XML or JSON */
363
+ GDPR_Audit_Log::log( $user->ID, sprintf( esc_html__( 'User downloaded all their data in %s format.', 'gdpr' ), $format ) );
364
+ $this->file_export_data( $user->user_email, $format, $key );
365
+ break;
366
+ }
367
+ }
368
+ }
369
+
370
+ /**
371
+ * Downloads the user data export in the chosen format.
372
+ * @since 1.2.0
373
+ * @param string $email The recipient.
374
+ * @param string $format The export format. XML or JSON.
375
+ * @param string $key The request array key.
376
+ */
377
+ private function file_export_data( $email, $format, $key ) {
378
+ $email = sanitize_email( $email );
379
+ $format = sanitize_text_field( wp_unslash( $format ) );
380
+ $key = sanitize_text_field( wp_unslash( $key ) );
381
+
382
+ $export = GDPR::generate_export( $email, $format );
383
+ if ( $export ) {
384
+ parent::remove_from_requests( $key );
385
+ header('Content-Type: application/octet-stream');
386
+ header('Content-Description: File Transfer');
387
+ header('Content-Disposition: attachment; filename=' . $email . '.' . $format);
388
+ echo $export;
389
+ }
390
+ die();
391
+ }
392
+ }
public/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden
public/partials/complaint-form.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <form class="gdpr-add-to-complaint-requests" action="<?php echo esc_url( admin_url('admin-post.php') ); ?>" method="post">
2
+ <?php wp_nonce_field( 'gdpr-add-to-requests', 'gdpr_request_nonce' ); ?>
3
+ <input type="hidden" name="action" value="gdpr_send_request_email">
4
+ <input type="hidden" name="type" value="complaint">
5
+ <?php if ( ! is_user_logged_in() ): ?>
6
+ <input type="email" name="user_email" placeholder="user@domain.com" required>
7
+ <?php endif ?>
8
+ <textarea name="data" rows="5" required></textarea>
9
+
10
+ <?php GDPR_Public::add_recaptcha(); ?>
11
+ <?php $submit_button_text = apply_filters( "gdpr_complaint_request_form_submit_text", esc_attr__( 'Submit', 'gdpr' ) ); ?>
12
+ <input type="submit" value="<?php echo esc_attr( $submit_button_text ); ?>">
13
+ </form>
public/partials/confirmation-screens.php ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The confirmation dialogs.
4
+ *
5
+ * @link https://trewknowledge.com
6
+ * @since 1.0.0
7
+ *
8
+ * @package GDPR
9
+ * @subpackage public
10
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
11
+ */
12
+ ?>
13
+
14
+ <div class="gdpr gdpr-general-confirmation gdpr-delete-confirmation">
15
+ <div class="gdpr-wrapper">
16
+ <header>
17
+ <div class="gdpr-box-title">
18
+ <h3><?php esc_attr_e( 'Close your account?', 'gdpr' ); ?></h3>
19
+ <span class="gdpr-close"></span>
20
+ </div>
21
+ </header>
22
+ <div class="gdpr-content">
23
+ <p><?php esc_html_e( 'Your account will be closed and all data will be permanently deleted and cannot be recovered. Are you sure?', 'gdpr' ); ?></p>
24
+ </div>
25
+ <footer>
26
+ <button class="gdpr-delete-account">Continue</button>
27
+ <button class="gdpr-cancel"><?php esc_html_e( 'Cancel', 'gdpr' ); ?></button>
28
+ </footer>
29
+ </div>
30
+ </div>
31
+
32
+ <?php if ( isset( $_GET['notify'] ) && $_GET['notify'] ) : ?>
33
+ <?php
34
+ $title = __( 'Error!', 'gdpr' );
35
+ if ( isset( $_GET['user-deleted'] ) ) {
36
+ $title = __( 'Your account', 'gdpr' );
37
+ if ( $_GET['user-deleted'] ) {
38
+ $text = __( 'Your account has been closed. We are sorry to see you go.', 'gdpr' );
39
+ } else {
40
+ $text = __( 'Your request has been received and is being reviewed. You will receive an email when we are done.', 'gdpr' );
41
+ }
42
+ }
43
+ if ( isset( $_GET['email-sent'] ) && $_GET['email-sent'] ) {
44
+ $title = __( 'Email confirmation', 'gdpr' );
45
+ $text = __( 'We\'ve sent you a confirmation email.', 'gdpr' );
46
+ }
47
+ if ( isset( $_GET['user-not-found'] ) && $_GET['user-not-found'] ) {
48
+ $text = __( 'User not found.', 'gdpr' );
49
+ }
50
+ if ( isset( $_GET['cannot-delete'] ) && $_GET['cannot-delete'] ) {
51
+ $text = __( 'We can\'t delete this user.', 'gdpr' );
52
+ }
53
+ if ( isset( $_GET['required-information-missing'] ) && $_GET['required-information-missing'] ) {
54
+ $text = __( 'Required information is missing from the form.', 'gdpr' );
55
+ }
56
+ if ( isset( $_GET['request-confirmed'] ) && $_GET['request-confirmed'] ) {
57
+ $title = __( 'Request Received', 'gdpr' );
58
+ $text = __( 'Your request has been received. We will be in touch soon.', 'gdpr' );
59
+ }
60
+ if ( isset( $_GET['error'] ) && $_GET['error'] ) {
61
+ $text = __( 'There was a problem with your request. Please try again later.', 'gdpr' );
62
+ }
63
+ ?>
64
+ <div class="gdpr gdpr-general-confirmation gdpr-accept-confirmation">
65
+ <div class="gdpr-wrapper">
66
+ <header>
67
+ <div class="gdpr-box-title">
68
+ <h3><?php echo esc_attr( $title ); ?></h3>
69
+ <span class="gdpr-close"></span>
70
+ </div>
71
+ </header>
72
+ <div class="gdpr-content">
73
+ <p><?php echo esc_html( $text ); ?></p>
74
+ </div>
75
+ <footer>
76
+ <button class="gdpr-ok">OK</button>
77
+ </footer>
78
+ </div>
79
+ </div>
80
+ <?php endif ?>
public/partials/delete-form.php ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * The add to deletion requests form markup.
4
+ *
5
+ * @link https://trewknowledge.com
6
+ * @since 1.0.0
7
+ *
8
+ * @package GDPR
9
+ * @subpackage public
10
+ * @author Fernando Claussen <fernandoclaussen@gmail.com>
11
+ */
12
+ ?>
13
+
14
+ <form class="gdpr-add-to-deletion-requests" action="<?php echo esc_url( admin_url('admin-post.php') ); ?>" method="post">
15
+ <?php wp_nonce_field( 'gdpr-add-to-requests', 'gdpr_request_nonce' ); ?>
16
+ <input type="hidden" name="action" value="gdpr_send_request_email">
17
+ <input type="hidden" name="type" value="delete">
18
+ <?php if ( ! is_user_logged_in() ): ?>
19
+ <input type="email" name="user_email" placeholder="user@domain.com" required>
20
+ <?php endif ?>
21
+ <?php GDPR_Public::add_recaptcha(); ?>
22
+ <?php $submit_button_text = apply_filters( "gdpr_delete_request_form_submit_text", esc_attr__( 'Close my account', 'gdpr' ) ); ?>
23
+ <input type="submit" value="<?php echo esc_attr( $submit_button_text ); ?>">
24
+ </form>
public/partials/export-data-form.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <form class="gdpr-export-data-form" action="<?php echo esc_url( admin_url('admin-post.php') ); ?>" method="post">
2
+ <?php wp_nonce_field( 'gdpr-add-to-requests', 'gdpr_request_nonce' ); ?>
3
+ <input type="hidden" name="action" value="gdpr_send_request_email">
4
+ <input type="hidden" name="type" value="export-data">
5
+ <?php if ( ! is_user_logged_in() ): ?>
6
+ <input type="email" name="user_email" placeholder="user@domain.com" required>
7
+ <?php endif ?>
8
+ <?php GDPR_Public::add_recaptcha(); ?>
9
+ <?php $submit_button_text = apply_filters( "gdpr_export_data_request_form_submit_text", esc_attr__( 'Download my data', 'gdpr' ) ); ?>
10
+ <input type="submit" value="<?php echo esc_attr( $submit_button_text ); ?>">
11
+ </form>
public/partials/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden
public/partials/privacy-bar.php ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * This file is used to markup the cookie bar.
5
+ *
6
+ *
7
+ * @link https://trewknowledge.com
8
+ * @since 1.0.0
9
+ *
10
+ * @package GDPR
11
+ * @subpackage public/partials
12
+ */
13
+ ?>
14
+
15
+ <div class="gdpr gdpr-privacy-bar" style="display:none;">
16
+ <div class="gdpr-wrapper">
17
+ <div class="gdpr-content">
18
+ <p>
19
+ <?php echo nl2br( wp_kses_post( $content ) ); ?>
20
+ </p>
21
+ </div>
22
+ <div class="gdpr-right">
23
+ <button class="gdpr-preferences" type="button"><?php esc_html_e( 'Privacy Preferences', 'gdpr' ); ?></button>
24
+ <button class="gdpr-agreement" type="button"><?php echo esc_html( $button_text ); ?></button>
25
+ </div>
26
+ </div>
27
+ </div>
public/partials/privacy-preferences-modal.php ADDED
@@ -0,0 +1,142 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * This file is used to markup the cookie preferences window.
5
+ *
6
+ *
7
+ * @link https://trewknowledge.com
8
+ * @since 1.0.0
9
+ *
10
+ * @package GDPR
11
+ * @subpackage public/partials
12
+ */
13
+ ?>
14
+
15
+ <div class="gdpr gdpr-privacy-preferences">
16
+ <div class="gdpr-wrapper">
17
+ <form method="post" class="gdpr-privacy-preferences-frm" action="<?php echo esc_url( admin_url('admin-post.php') ); ?>">
18
+ <input type="hidden" name="action" value="gdpr_update_privacy_preferences">
19
+ <?php wp_nonce_field( 'gdpr-update_privacy_preferences', 'update-privacy-preferences-nonce' ); ?>
20
+ <header>
21
+ <div class="gdpr-box-title">
22
+ <h3><?php esc_html_e( 'Privacy Preference Center', 'gdpr' ); ?></h3>
23
+ <span class="gdpr-close"></span>
24
+ </div>
25
+ </header>
26
+ <div class="gdpr-mobile-menu">
27
+ <button type="button"><?php esc_html_e( 'Options', 'gdpr' ); ?></button>
28
+ </div>
29
+ <div class="gdpr-content">
30
+ <ul class="gdpr-tabs">
31
+ <li><button type="button" class="gdpr-tab-button gdpr-active" data-target="gdpr-consent-management"><?php esc_html_e( 'Consent Management', 'gdpr' ); ?></button></li>
32
+ <?php reset( $tabs ); ?>
33
+ <?php if ( ! empty( $tabs ) ): ?>
34
+ <li><button type="button" class="gdpr-tab-button gdpr-cookie-settings" data-target="<?php echo esc_attr( key( $tabs ) ); ?>"><?php esc_html_e( 'Cookie Settings', 'gdpr' ); ?></button>
35
+ <ul class="gdpr-subtabs">
36
+ <?php
37
+ foreach ( $tabs as $key => $tab ) {
38
+ echo '<li><button type="button" data-target="' . esc_attr( $key ) . '" ' . '>' . esc_html( $tab['name'] ) . '</button></li>';
39
+ }
40
+ ?>
41
+ </ul>
42
+ </li>
43
+ <?php endif ?>
44
+ </ul>
45
+ <div class="gdpr-tab-content">
46
+ <div class="gdpr-consent-management gdpr-active">
47
+ <header>
48
+ <h4><?php esc_html_e( 'Consent Management', 'gdpr' ); ?></h4>
49
+ </header>
50
+ <div class="gdpr-info">
51
+ <p><?php echo nl2br( esc_html( $cookie_privacy_excerpt ) ); ?></p>
52
+ <?php foreach ( $consent_types as $consent_key => $type ) : ?>
53
+ <div class="gdpr-cookies-used">
54
+ <div class="gdpr-cookie-title">
55
+ <p><?php echo esc_html( $type['name'] ); ?></p>
56
+ <?php if ( $type['required'] ) : ?>
57
+ <span class="gdpr-always-active"><?php esc_html_e( 'Required', 'gdpr' ); ?></span>
58
+ <input type="hidden" name="user_consents[]" value="<?php echo esc_attr( $consent_key ); ?>" checked style="display:none;">
59
+ <?php else : ?>
60
+ <label class="gdpr-switch">
61
+ <input type="checkbox" name="user_consents[]" value="<?php echo esc_attr( $consent_key ); ?>" <?php echo ! empty( $user_consents ) ? checked( in_array( $consent_key, $user_consents, true ), 1, false ) : ''; ?>>
62
+ <span class="gdpr-slider round"></span>
63
+ </label>
64
+ <?php endif; ?>
65
+ </div>
66
+ <div class="gdpr-cookies">
67
+ <span><?php echo wp_kses( $type['description'], $this->allowed_html ); ?></span>
68
+ </div>
69
+ </div>
70
+ <?php endforeach; ?>
71
+ </div>
72
+ </div>
73
+ <?php $all_cookies = array(); ?>
74
+ <?php foreach ( $tabs as $key => $tab ) : ?>
75
+ <div class="<?php echo esc_attr( $key ); ?>">
76
+ <header>
77
+ <h4><?php echo esc_html( $tab['name'] ); ?></h4>
78
+ </header><!-- /header -->
79
+ <div class="gdpr-info">
80
+ <p><?php echo nl2br( $tab['how_we_use'] ); ?></p>
81
+ <?php if ( isset( $tab['cookies_used'] ) && $tab['cookies_used'] ) : ?>
82
+ <div class="gdpr-cookies-used">
83
+ <div class="gdpr-cookie-title">
84
+ <p><?php esc_html_e( 'Cookies Used', 'gdpr' ); ?></p>
85
+ <?php
86
+ $site_cookies = array();
87
+ $enabled = false;
88
+ $cookies_used = explode( ',', $tab['cookies_used'] );
89
+ $approved_cookies = isset( $_COOKIE['gdpr']['allowed_cookies'] ) ? json_decode( wp_unslash( $_COOKIE['gdpr']['allowed_cookies'] ) ) : array();
90
+ foreach ( $cookies_used as $cookie ) {
91
+ $site_cookies[] = trim( $cookie );
92
+ $all_cookies[] = trim( $cookie );
93
+ if ( ! empty( $approved_cookies ) ) {
94
+ if ( in_array( trim( $cookie ), $approved_cookies ) ) {
95
+ $enabled = true;
96
+ }
97
+ }
98
+ }
99
+ ?>
100
+ <?php if ( $tab['always_active'] ) : ?>
101
+ <span class="gdpr-always-active"><?php esc_html_e( 'Always Active', 'gdpr' ); ?></span>
102
+ <input type="hidden" name="approved_cookies[]" value="<?php echo esc_attr( json_encode( $site_cookies ) ) ?>" checked>
103
+ <?php else: ?>
104
+ <label class="gdpr-switch">
105
+ <input type="checkbox" name="approved_cookies[]" value="<?php echo esc_attr( json_encode( $site_cookies ) ) ?>" <?php checked( $enabled, true ); ?>>
106
+ <span class="gdpr-slider round"></span>
107
+ </label>
108
+ <?php endif; ?>
109
+ </div>
110
+ <div class="gdpr-cookies">
111
+ <span><?php echo esc_html( $tab['cookies_used'] ); ?></span>
112
+ </div>
113
+ </div>
114
+ <?php endif ?>
115
+ <?php if ( isset( $tab['hosts'] ) && ! empty( $tab['hosts'] ) ) : ?>
116
+ <?php foreach ( $tab['hosts'] as $host_key => $host ) : ?>
117
+ <div class="gdpr-cookies-used">
118
+ <div class="gdpr-cookie-title">
119
+ <p><?php echo esc_html( $host['name'] ); ?></p>
120
+ <a href="<?php echo esc_url( $host['optout'] ); ?>" target="_blank" class="gdpr-button"><?php esc_html_e( 'Opt Out', 'gdpr' ); ?></a>
121
+ </div>
122
+ <div class="gdpr-cookies">
123
+ <span><?php echo esc_html( $host['cookies_used'] ); ?></span>
124
+ </div>
125
+ </div>
126
+ <?php endforeach ?>
127
+ <?php endif ?>
128
+ </div>
129
+ </div>
130
+ <?php endforeach; ?>
131
+ </div>
132
+ <input type="hidden" name="all_cookies" value="<?php echo esc_attr( json_encode( $all_cookies ) ); ?>">
133
+ </div>
134
+ <footer>
135
+ <input type="submit" value="<?php esc_attr_e( 'Save Preferences', 'gdpr' ); ?>">
136
+ <?php if ( $privacy_policy_page ) : ?>
137
+ <span><a href="<?php echo esc_url( get_permalink( $privacy_policy_page ) ); ?>" target="_blank"><?php esc_html_e( 'More Information', 'gdpr' ); ?></a></span>
138
+ <?php endif ?>
139
+ </footer>
140
+ </form>
141
+ </div>
142
+ </div>
public/partials/reconsent-modal.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="gdpr-reconsent-modal" style="display:none;">
2
+ <div class="gdpr-reconsent-modal-content">
3
+ <h3><?php esc_html_e( 'Our Privacy Policy has been updated.', 'gdpr' ); ?></h3>
4
+ <h4><?php esc_html_e( 'To continue using the site you need to read the revised version and agree to the terms.', 'gdpr' ); ?></h4>
5
+ <div class="gdpr-privacy-viewer">
6
+ <?php echo apply_filters( 'the_content', $page_obj->post_content ); ?>
7
+ </div>
8
+ <div class="gdpr-consent-buttons">
9
+ <a href="#" class="gdpr-agree" data-nonce="<?php echo esc_attr( wp_create_nonce( 'gdpr-user_agree_with_terms' ) ); ?>"><?php esc_html_e( 'Agree', 'gdpr' ); ?></a>
10
+ <a href="#" class="gdpr-disagree"><?php esc_html_e( 'Disagree', 'gdpr' ); ?></a>
11
+ </div>
12
+ <div class="gdpr-consent-loading">
13
+ <p class="gdpr-loading"><span class="gdpr-updating"><?php esc_html_e( 'Updating', 'gdpr' ); ?></span><span class="gdpr-ellipsis"></span></p>
14
+ </div>
15
+ </div>
16
+ </div>
17
+
18
+ <div class="gdpr gdpr-general-confirmation gdpr-disagree-confirmation">
19
+ <div class="gdpr-wrapper">
20
+ <header>
21
+ <div class="gdpr-box-title">
22
+ <h3><?php esc_attr_e( 'Are you sure?', 'gdpr' ); ?></h3>
23
+ <span class="gdpr-close"></span>
24
+ </div>
25
+ </header>
26
+ <div class="gdpr-content">
27
+ <p><?php esc_html_e( 'By disagreeing you will no longer have access to our site and will be logged out.', 'gdpr' ); ?></p>
28
+ </div>
29
+ <footer>
30
+ <button class="gdpr-disagree-confirm" data-nonce="<?php echo esc_attr( wp_create_nonce( 'gdpr-user_disagree_with_terms' ) ); ?>"><?php esc_html_e( 'Continue', 'gdpr' ); ?></button>
31
+ <button class="gdpr-cancel"><?php esc_html_e( 'Cancel', 'gdpr' ); ?></button>
32
+ </footer>
33
+ </div>
34
+ </div>
public/partials/rectify-form.php ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <form class="gdpr-add-to-rectify-requests" action="<?php echo esc_url( admin_url('admin-post.php') ); ?>" method="post">
2
+ <?php wp_nonce_field( 'gdpr-add-to-requests', 'gdpr_request_nonce' ); ?>
3
+ <input type="hidden" name="action" value="gdpr_send_request_email">
4
+ <input type="hidden" name="type" value="rectify">
5
+ <?php if ( ! is_user_logged_in() ): ?>
6
+ <input type="email" name="user_email" placeholder="user@domain.com" required>
7
+ <?php endif ?>
8
+ <textarea name="data" rows="5" required></textarea>
9
+ <?php GDPR_Public::add_recaptcha(); ?>
10
+ <?php $submit_button_text = apply_filters( "gdpr_rectify_request_form_submit_text", esc_attr__( 'Submit', 'gdpr' ) ); ?>
11
+ <input type="submit" value="<?php echo esc_attr( $submit_button_text ); ?>">
12
+ </form>
templates/email/complaint-request.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ echo sprintf(
3
+ /* translators: 1: The complaint content, 2: confirmation link, 3: reset password link */
4
+ esc_html__(
5
+ 'Someone placed a complaint on your behalf on our site.
6
+ By clicking confirm a request will be made and we will do our best to fulfil it.
7
+
8
+ --------------------------------------------------------
9
+ Request
10
+ --------------------------------------------------------
11
+ %s
12
+
13
+
14
+
15
+
16
+ To confirm this request, click here: %s
17
+
18
+
19
+
20
+ ---------------------------------------------------------------------------------
21
+ If that wasn\'t you, reset your password: %s
22
+ ', 'gdpr' ),
23
+ esc_html( $args['data'] ),
24
+ esc_url_raw( $args['confirm_url'] ),
25
+ esc_url_raw( $args['forgot_password_url'] )
26
+ );
templates/email/complaint-resolved.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ echo esc_html__(
4
+ 'We resolved your complaint request.
5
+ If you have any problems or questions, don\'t hesitate to contact us.', 'gdpr' );
templates/email/data-breach-notification.php ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ echo sprintf(
3
+ /* translators: 1: Email content, 2: Nature of data breach, 3: Contact details for data protection officer, 4: Likely consequences of breach, 5: Measures taken */
4
+ esc_html__(
5
+ '%s
6
+
7
+ --------------------------------------------------------
8
+ Nature of the personal data breach:
9
+ --------------------------------------------------------
10
+ %s
11
+
12
+ --------------------------------------------------------
13
+ Name and contact details of the data protection officer:
14
+ --------------------------------------------------------
15
+ %s
16
+
17
+ --------------------------------------------------------
18
+ Likely consequences of the personal data breach:
19
+ --------------------------------------------------------
20
+ %s
21
+
22
+ --------------------------------------------------------
23
+ Measures taken or proposed to be taken:
24
+ --------------------------------------------------------
25
+ %s
26
+ ', 'gdpr' ),
27
+ esc_html( $args['content'] ),
28
+ esc_html( $args['nature'] ),
29
+ esc_html( $args['office_contact'] ),
30
+ esc_html( $args['consequences'] ),
31
+ esc_html( $args['measures'] ),
32
+ esc_url_raw( $args['confirm_url'] )
33
+ );
34
+ ?>
templates/email/data-breach-request.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ echo sprintf(
3
+ /* translators: 1: User who requested the notification, 2: Nature of data breach, 3: Contact details for data protection officer, 4: Likely consequences of breach, 5: Measures taken, 6: Confirmation link */
4
+ esc_html__(
5
+ 'A request to send a mass email notification to all users regarding a data breach has been made by %s.
6
+
7
+ --------------------------------------------------------
8
+ Nature of the personal data breach:
9
+ --------------------------------------------------------
10
+ %s
11
+
12
+ --------------------------------------------------------
13
+ Name and contact details of the data protection officer:
14
+ --------------------------------------------------------
15
+ %s
16
+
17
+ --------------------------------------------------------
18
+ Likely consequences of the personal data breach:
19
+ --------------------------------------------------------
20
+ %s
21
+
22
+ --------------------------------------------------------
23
+ Measures taken or proposed to be taken:
24
+ --------------------------------------------------------
25
+ %s
26
+
27
+
28
+ To confirm this request, click here: %s
29
+
30
+ ---------------------------------------------------------------------------------
31
+ If that is not intended, have the person who requested it change their password.
32
+ ---------------------------------------------------------------------------------
33
+ ', 'gdpr' ),
34
+ esc_html( $args['requester'] ),
35
+ esc_html( $args['nature'] ),
36
+ esc_html( $args['office_contact'] ),
37
+ esc_html( $args['consequences'] ),
38
+ esc_html( $args['measures'] ),
39
+ esc_url_raw( $args['confirm_url'] )
40
+ );
41
+ ?>
templates/email/delete-request.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ echo sprintf(
3
+ /* translators: 1: Confirmation link, 2: Reset password link */
4
+ esc_html__(
5
+ 'Someone placed a request for your information to be removed from our site.
6
+ By clicking confirm your account will be removed from our site and all data we collected
7
+ over time will be erased from our database. It will be impossible for us to retrieve that
8
+ information in the future.
9
+
10
+
11
+
12
+ To confirm this request, click here: %s
13
+
14
+
15
+
16
+ ---------------------------------------------------------------------------------
17
+ If that wasn\'t you, reset your password: %s
18
+ ', 'gdpr' ),
19
+ esc_url_raw( $args['confirm_url'] ),
20
+ esc_url_raw( $args['forgot_password_url'] )
21
+ );
templates/email/delete-resolved.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ echo sprintf(
4
+ /* translators: 6-digit token for audit log */
5
+ esc_html__(
6
+ 'Your account has been closed.
7
+
8
+ We no longer hold any information about you.
9
+ If you ever need to make a complaint you can email us and we will try to help you.
10
+ To be able to make a complaint you will be requested to provide your email address and the token below.
11
+
12
+ %s', 'gdpr' ),
13
+ $args['token']
14
+ );
templates/email/export-data-request.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ echo sprintf(
3
+ /* translators: 1: XML download link, 2: JSON download link, 3: reset password link */
4
+ esc_html__(
5
+ 'Someone requested to download your data from our site.
6
+ By clicking confirm we will redirect you back to our site where a download will begin.
7
+
8
+ To download it in a XML format, click here: %s
9
+ To download it in a JSON format, click here: %s
10
+
11
+
12
+
13
+ ---------------------------------------------------------------------------------
14
+ If that wasn\'t you, reset your password: %s
15
+ ', 'gdpr' ),
16
+ esc_url_raw( $args['confirm_url_xml'] ),
17
+ esc_url_raw( $args['confirm_url_json'] ),
18
+ esc_url_raw( $args['forgot_password_url'] )
19
+ );
templates/email/index.php ADDED
@@ -0,0 +1 @@
 
1
+ <?php // Silence is golden
templates/email/new-request.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ echo sprintf(
4
+ /* translators: 1: The type of request. 2: Link to where the request can be reviewed. */
5
+ esc_html__(
6
+ 'There is a new %1$s request waiting for review.
7
+
8
+ Review your requests: %2$s', 'gdpr' ),
9
+ esc_html( $args['type'] ),
10
+ esc_url_raw( $args['review_url'] )
11
+ );
templates/email/rectify-request.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ echo sprintf(
3
+ /* translators: 1: The request content, 2: confirmation link, 3: reset password link */
4
+ esc_html__(
5
+ 'Someone placed a request for your information to be rectified on our site.
6
+ By clicking confirm a request will be made and we will do our best to fulfil it.
7
+
8
+ --------------------------------------------------------
9
+ Request
10
+ --------------------------------------------------------
11
+ %s
12
+
13
+
14
+
15
+
16
+ To confirm this request, click here: %s
17
+
18
+
19
+
20
+ ---------------------------------------------------------------------------------
21
+ If that wasn\'t you, reset your password: %s
22
+ ', 'gdpr' ),
23
+ esc_html( $args['data'] ),
24
+ esc_url_raw( $args['confirm_url'] ),
25
+ esc_url_raw( $args['forgot_password_url'] )
26
+ );
templates/email/rectify-resolved.php ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
1
+ <?php
2
+
3
+ echo esc_html__(
4
+ 'We resolved your rectification request.
5
+ If you have any problems or questions, don\'t hesitate to contact us.', 'gdpr' );
uninstall.php ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Fired when the plugin is uninstalled.
5
+ *
6
+ * When populating this file, consider the following flow
7
+ * of control:
8
+ *
9
+ * - This method should be static
10
+ * - Check if the $_REQUEST content actually is the plugin name
11
+ * - Run an admin referrer check to make sure it goes through authentication
12
+ * - Verify the output of $_GET makes sense
13
+ * - Repeat with other user roles. Best directly by using the links/query string parameters.
14
+ * - Repeat things for multisite. Once for a single site in the network, once sitewide.
15
+ *
16
+ * This file may be updated more in future version of the Boilerplate; however, this is the
17
+ * general skeleton and outline for how the file should work.
18
+ *
19
+ * For more information, see the following discussion:
20
+ * https://github.com/tommcfarlin/WordPress-Plugin-Boilerplate/pull/123#issuecomment-28541913
21
+ *
22
+ * @link https://trewknowledge.com
23
+ * @since 1.0.0
24
+ *
25
+ * @package GDPR
26
+ */
27
+
28
+ // If uninstall not called from WordPress, then exit.
29
+ if ( ! defined( 'WP_UNINSTALL_PLUGIN' ) ) {
30
+ exit;
31
+ }