Linux webm002.cluster120.gra.hosting.ovh.net 6.18.42-ovh-vps-grsec-zfs+ #1 SMP PREEMPT_DYNAMIC Wed Aug 5 15:59:48 CEST 2026 x86_64
Apache
: 10.120.20.2 | : 216.73.216.49
Cant Read [ /etc/named.conf ]
8.5.7
verseaumee
Terminal
AUTO ROOT
Adminer
Backdoor Destroyer
Linux Exploit
Lock Shell
Lock File
Create User
CREATE RDP
PHP Mailer
BACKCONNECT
UNLOCK SHELL
HASH IDENTIFIER
README
+ Create Folder
+ Create File
/
home /
verseaumee /
123click /
assets /
[ HOME SHELL ]
Name
Size
Permission
Action
css
[ DIR ]
drwx---r-x
fonts
[ DIR ]
drwx---r-x
images
[ DIR ]
drwx---r-x
js
[ DIR ]
drwx---r-x
.bash_logout.bash_logout.tar.g...
126
B
-rw-r--r--
.bash_logout.tar
2
KB
-rw-r--r--
.bash_profile.bash_profile.tar...
273
B
-rw-r--r--
.bash_profile.tar
2
KB
-rw-r--r--
.bashrc.bashrc.tar.gz
190
B
-rw-r--r--
.bashrc.tar
2
KB
-rw-r--r--
.ftpquota.ftpquota.tar.gz
135
B
-rw-r--r--
.ftpquota.tar
2
KB
-rw-r--r--
.hta.hta.tar.gz
3.15
KB
-rw-r--r--
.hta.tar
6
KB
-rw-r--r--
.htaccess.bak.htaccess.bak.tar...
428
B
-rw-r--r--
.htaccess.bak.tar
2.5
KB
-rw-r--r--
.htaccess.htaccess.tar.gz
194
B
-rw-r--r--
.htaccess.tar
41.5
KB
-rw-r--r--
.htaccess_10125.htaccess_10125...
1.93
KB
-rw-r--r--
.htaccess_10125.tar
6
KB
-rw-r--r--
.htaccess_2608.htaccess_2608.t...
1.83
KB
-rw-r--r--
.htaccess_2608.tar
5.5
KB
-rw-r--r--
.htaccess_old.htaccess_old.tar...
1.71
KB
-rw-r--r--
.htaccess_old.tar
9.5
KB
-rw-r--r--
.htaccess_old2.htaccess_old2.t...
1.81
KB
-rw-r--r--
.htaccess_old2.tar
5.5
KB
-rw-r--r--
.htaccessx.htaccessx.tar.gz
479
B
-rw-r--r--
.htaccessx.tar
2.5
KB
-rw-r--r--
.htaccessxx.htaccessxx.tar.gz
427
B
-rw-r--r--
.htaccessxx.tar
2.5
KB
-rw-r--r--
.mad-root
0
B
-rw-r--r--
.ovhconfig.ovhconfig.tar.gz
188
B
-rw-r--r--
.ovhconfig.tar
2
KB
-rw-r--r--
.tmb.tar
221
KB
-rw-r--r--
.tmb.tar.gz
41.33
KB
-rw-r--r--
.tmb.zip
201.6
KB
-rw-r--r--
.user.ini.tar
3
KB
-rw-r--r--
.user.ini.user.ini.tar.gz
220
B
-rw-r--r--
.well-known.tar
158.5
KB
-rw-r--r--
.well-known.tar.gz
69.8
KB
-rw-r--r--
.well-known.zip
152.89
KB
-rw-r--r--
----index.html.html.tar.gz
8.28
KB
-rw-r--r--
----index.html.tar
40
KB
-rw-r--r--
01.jpg.jpg.tar.gz
2.27
KB
-rw-r--r--
01.jpg.tar
4
KB
-rw-r--r--
10pourcent.zip
212.54
MB
-rw-r--r--
404.html.html.tar.gz
1.19
KB
-rw-r--r--
404.html.tar
5
KB
-rw-r--r--
888index.php.php.tar.gz
8.52
KB
-rw-r--r--
888index.php.tar
42.5
KB
-rw-r--r--
ANE_MOULIN.jpg.jpg.tar.gz
12.53
KB
-rw-r--r--
ANE_MOULIN.jpg.tar
14
KB
-rw-r--r--
Association.zip
6.54
KB
-rw-r--r--
CALES.jpg.jpg.tar.gz
13.84
KB
-rw-r--r--
CALES.jpg.tar
15.5
KB
-rw-r--r--
CONDITIONS.pdf.pdf.tar.gz
592.22
KB
-rw-r--r--
CONDITIONS.pdf.tar
753.5
KB
-rw-r--r--
Cache.zip
39.59
KB
-rw-r--r--
Demo.tar
17.37
MB
-rw-r--r--
Demo.tar.gz
16.59
MB
-rw-r--r--
Demo.zip
17.31
MB
-rw-r--r--
Diff.php.php.tar.gz
3.15
KB
-rw-r--r--
Diff.php.tar
14.5
KB
-rw-r--r--
Downloads.tar
868.5
KB
-rw-r--r--
Downloads.tar.gz
790.18
KB
-rw-r--r--
Downloads.zip
865.8
KB
-rw-r--r--
Filter.zip
41.03
KB
-rw-r--r--
FontAwesome.otf.otf.tar.gz
108.08
KB
-rw-r--r--
FontAwesome.otf.tar
133.5
KB
-rw-r--r--
HTTP.tar
13
KB
-rw-r--r--
HTTP.tar.gz
3.32
KB
-rw-r--r--
ID3.tar
1.12
MB
-rw-r--r--
ID3.tar.gz
233.46
KB
-rw-r--r--
ID3.zip
1.1
MB
-rw-r--r--
IXR.tar
43
KB
-rw-r--r--
IXR.tar.gz
7.69
KB
-rw-r--r--
IXR.zip
34.92
KB
-rw-r--r--
JEANJEAN.jpg.jpg.tar.gz
14.03
KB
-rw-r--r--
JEANJEAN.jpg.tar
15.5
KB
-rw-r--r--
LA.png.png.tar.gz
3.38
KB
-rw-r--r--
LA.png.tar
5
KB
-rw-r--r--
LICENSE.txt.tar
93.5
KB
-rw-r--r--
LICENSE.txt.txt.tar.gz
6.78
KB
-rw-r--r--
LOGO A LA MAZE ON 17 NOVEMBRE ...
1.36
MB
-rw-r--r--
LOGO A LA MAZE ON 17 NOVEMBRE ...
1.41
MB
-rw-r--r--
LOGO ARCEP 2.jpg.jpg.tar.gz
399.9
KB
-rw-r--r--
LOGO ARCEP 2.jpg.tar
1.38
MB
-rw-r--r--
LOGO ARCEP 2.psd.psd.tar.gz
3.66
MB
-rw-r--r--
LOGO ARCEP 2.psd.tar
9.75
MB
-rw-r--r--
Log.tar
45
KB
-rw-r--r--
Log.tar.gz
8.58
KB
-rw-r--r--
Log.zip
37.62
KB
-rw-r--r--
Net.tar
9
KB
-rw-r--r--
Net.tar.gz
2.68
KB
-rw-r--r--
PHPMailer.tar
227.5
KB
-rw-r--r--
PHPMailer.tar.gz
47.75
KB
-rw-r--r--
PHPMailer.zip
223.7
KB
-rw-r--r--
PTITE_FERME.png.png.tar.gz
70.39
KB
-rw-r--r--
PTITE_FERME.png.tar
73.5
KB
-rw-r--r--
PayPal.PNG.PNG.tar.gz
18.47
KB
-rw-r--r--
PayPal.PNG.tar
24
KB
-rw-r--r--
README.txt.tar
34
KB
-rw-r--r--
README.txt.txt.tar.gz
1.83
KB
-rw-r--r--
Requests.tar
427
KB
-rw-r--r--
Requests.tar.gz
30.97
KB
-rw-r--r--
Requests.zip
220.53
KB
-rw-r--r--
SimplePie.tar
474
KB
-rw-r--r--
SimplePie.tar.gz
77.41
KB
-rw-r--r--
SimplePie.zip
456.69
KB
-rw-r--r--
TF89388.tar
958.4
MB
-rw-r--r--
TF89388.tar.gz
20
B
-rw-r--r--
Tate-tuker-web.jpg.jpg.tar.gz
97.22
KB
-rw-r--r--
Tate-tuker-web.jpg.tar
101
KB
-rw-r--r--
Text.tar
63.5
KB
-rw-r--r--
Text.tar.gz
12.67
KB
-rw-r--r--
Text.zip
57.09
KB
-rw-r--r--
UA.png.png.tar.gz
3.24
KB
-rw-r--r--
UA.png.tar
5
KB
-rw-r--r--
XML.zip
7.11
KB
-rw-r--r--
_contact.html.html.tar.gz
4.61
KB
-rw-r--r--
_contact.html.tar
17.5
KB
-rw-r--r--
_index.html.html.tar.gz
8.52
KB
-rw-r--r--
_index.html.tar
40.5
KB
-rw-r--r--
ab1.jpg.jpg.tar.gz
19.2
KB
-rw-r--r--
ab1.jpg.tar
21
KB
-rw-r--r--
ab2.jpg.jpg.tar.gz
19.28
KB
-rw-r--r--
ab2.jpg.tar
21
KB
-rw-r--r--
about-blog.jpg.jpg.tar.gz
61
KB
-rw-r--r--
about-blog.jpg.tar
65.5
KB
-rw-r--r--
about-us.jpg.jpg.tar.gz
27.54
KB
-rw-r--r--
about-us.jpg.tar
31
KB
-rw-r--r--
about.html.html.tar.gz
3.86
KB
-rw-r--r--
about.html.tar
38.5
KB
-rw-r--r--
about.php.php.tar.gz
5.35
KB
-rw-r--r--
about.php.tar
80
KB
-rw-r--r--
actionlog.tar
40
KB
-rw-r--r--
actionlog.tar.gz
4.46
KB
-rw-r--r--
actionlog.zip
36.47
KB
-rw-r--r--
actionlogs.tar
18.5
KB
-rw-r--r--
actionlogs.tar.gz
1.25
KB
-rw-r--r--
actionlogs.zip
2.82
KB
-rw-r--r--
acymailing.tar
338.5
KB
-rw-r--r--
acymailing.tar.gz
48.94
KB
-rw-r--r--
acymailing.zip
308.85
KB
-rw-r--r--
admin-ajax.php.php.tar.gz
1.89
KB
-rw-r--r--
admin-ajax.php.tar
7
KB
-rw-r--r--
admin-bar.php.php.tar.gz
8.22
KB
-rw-r--r--
admin-bar.php.tar
38
KB
-rw-r--r--
admin-footer.php.php.tar.gz
1.14
KB
-rw-r--r--
admin-footer.php.tar
4.5
KB
-rw-r--r--
admin-functions.php.php.tar.gz
333
B
-rw-r--r--
admin-functions.php.tar
3
KB
-rw-r--r--
admin-get.php.php.tar.gz
980
B
-rw-r--r--
admin-get.php.tar
3.5
KB
-rw-r--r--
admin-header.php.php.tar.gz
2.97
KB
-rw-r--r--
admin-header.php.tar
30
KB
-rw-r--r--
admin-post.php.php.tar.gz
847
B
-rw-r--r--
admin-post.php.tar
4
KB
-rw-r--r--
admin.php.php.tar.gz
3.79
KB
-rw-r--r--
admin.php.tar
27
KB
-rw-r--r--
adminer.php
0
B
-rw-r--r--
administrator.tar
83.32
MB
-rw-r--r--
administrator.tar.gz
4.57
MB
-rw-r--r--
administrator.zip
41.74
MB
-rw-r--r--
afrikavio.jpg.jpg.tar.gz
300.69
KB
-rw-r--r--
afrikavio.jpg.tar
323.5
KB
-rw-r--r--
ai1wm-backups.tar
717.78
MB
-rw-r--r--
ai1wm-backups.tar.gz
20
B
-rw-r--r--
akismet.tar
258
KB
-rw-r--r--
akismet.tar.gz
67.37
KB
-rw-r--r--
akismet.zip
240.29
KB
-rw-r--r--
all.js.js.tar.gz
818
B
-rw-r--r--
all.js.tar
3.5
KB
-rw-r--r--
android-chrome-192x192.png.png...
5.04
KB
-rw-r--r--
android-chrome-192x192.png.tar
7
KB
-rw-r--r--
android-chrome-512x512.png.png...
18.64
KB
-rw-r--r--
android-chrome-512x512.png.tar
23.5
KB
-rw-r--r--
android-icon-144x144.png.png.t...
14.3
KB
-rw-r--r--
android-icon-144x144.png.tar
16
KB
-rw-r--r--
android-icon-192x192.png.png.t...
19.65
KB
-rw-r--r--
android-icon-192x192.png.tar
21.5
KB
-rw-r--r--
android-icon-36x36.png.png.tar...
2.67
KB
-rw-r--r--
android-icon-36x36.png.tar
4.5
KB
-rw-r--r--
android-icon-48x48.png.png.tar...
3.76
KB
-rw-r--r--
android-icon-48x48.png.tar
5.5
KB
-rw-r--r--
android-icon-72x72.png.png.tar...
5.93
KB
-rw-r--r--
android-icon-72x72.png.tar
7.5
KB
-rw-r--r--
android-icon-96x96.png.png.tar...
8.36
KB
-rw-r--r--
android-icon-96x96.png.tar
10
KB
-rw-r--r--
android.png.png.tar.gz
2.47
KB
-rw-r--r--
android.png.tar
4
KB
-rw-r--r--
angular-icon.svg.svg.tar.gz
559
B
-rw-r--r--
angular-icon.svg.tar
2.5
KB
-rw-r--r--
angular.png.png.tar.gz
10.65
KB
-rw-r--r--
angular.png.tar
13
KB
-rw-r--r--
api.tar
383.5
KB
-rw-r--r--
api.tar.gz
77.41
KB
-rw-r--r--
api.zip
345.17
KB
-rw-r--r--
app.js.js.tar.gz
501
B
-rw-r--r--
app.js.tar
2.5
KB
-rw-r--r--
app.php.php.tar.gz
1.05
KB
-rw-r--r--
app.php.tar
4
KB
-rw-r--r--
app.tar
199
KB
-rw-r--r--
app.tar.gz
34.34
KB
-rw-r--r--
app.zip
171.56
KB
-rw-r--r--
apple-icon-114x114.png.png.tar...
10.47
KB
-rw-r--r--
apple-icon-114x114.png.tar
12
KB
-rw-r--r--
apple-icon-144x144.png.png.tar...
14.3
KB
-rw-r--r--
apple-icon-144x144.png.tar
16
KB
-rw-r--r--
apple-icon-152x152.png.png.tar...
15.31
KB
-rw-r--r--
apple-icon-152x152.png.tar
17
KB
-rw-r--r--
apple-icon-180x180.png.png.tar...
19.25
KB
-rw-r--r--
apple-icon-180x180.png.tar
21
KB
-rw-r--r--
apple-icon-57x57.png.png.tar.g...
4.53
KB
-rw-r--r--
apple-icon-57x57.png.tar
6
KB
-rw-r--r--
apple-icon-60x60.png.png.tar.g...
4.77
KB
-rw-r--r--
apple-icon-60x60.png.tar
6.5
KB
-rw-r--r--
apple-icon-76x76.png.png.tar.g...
6.29
KB
-rw-r--r--
apple-icon-76x76.png.tar
8
KB
-rw-r--r--
apple-icon-precomposed.png.png...
20.06
KB
-rw-r--r--
apple-icon-precomposed.png.tar
22
KB
-rw-r--r--
apple-touch-icon.png.png.tar.g...
3.99
KB
-rw-r--r--
apple-touch-icon.png.tar
11
KB
-rw-r--r--
apple.png.png.tar.gz
3.5
KB
-rw-r--r--
apple.png.tar
5.5
KB
-rw-r--r--
apps.tar
266
KB
-rw-r--r--
apps.tar.gz
223.94
KB
-rw-r--r--
apps.zip
251.66
KB
-rw-r--r--
archives.tar
11
KB
-rw-r--r--
archives.tar.gz
826
B
-rw-r--r--
archives.zip
2.92
KB
-rw-r--r--
assets.tar
151.49
MB
-rw-r--r--
assets.tar.gz
101.02
MB
-rw-r--r--
assets.zip
88.82
MB
-rw-r--r--
async-upload.php.php.tar.gz
1.96
KB
-rw-r--r--
async-upload.php.tar
12
KB
-rw-r--r--
atomlib.php.php.tar.gz
3.27
KB
-rw-r--r--
atomlib.php.tar
13.5
KB
-rw-r--r--
attachments.tar
2
KB
-rw-r--r--
attachments.tar.gz
124
B
-rw-r--r--
audio.tar
707.5
KB
-rw-r--r--
audio.tar.gz
674.05
KB
-rw-r--r--
authentication.tar
51
KB
-rw-r--r--
authentication.tar.gz
8.78
KB
-rw-r--r--
authentication.zip
40.29
KB
-rw-r--r--
authorize-application.php.php....
3.16
KB
-rw-r--r--
authorize-application.php.tar
12
KB
-rw-r--r--
avatar.zip
3.48
KB
-rw-r--r--
banners.tar
35
KB
-rw-r--r--
banners.tar.gz
29.58
KB
-rw-r--r--
banners.zip
31.48
KB
-rw-r--r--
beez3.tar
630.5
KB
-rw-r--r--
beez3.tar.gz
264.69
KB
-rw-r--r--
beez3.zip
535.04
KB
-rw-r--r--
bg-testinomial.jpg.jpg.tar.gz
461.34
KB
-rw-r--r--
bg-testinomial.jpg.tar
927
KB
-rw-r--r--
bg.jpg.jpg.tar.gz
110.06
KB
-rw-r--r--
bg.jpg.tar
112
KB
-rw-r--r--
bg1.jpg.jpg.tar.gz
150.09
KB
-rw-r--r--
bg1.jpg.tar
152
KB
-rw-r--r--
bg2.jpg.jpg.tar.gz
124.53
KB
-rw-r--r--
bg2.jpg.tar
127.5
KB
-rw-r--r--
bg3.jpg.jpg.tar.gz
669.84
KB
-rw-r--r--
bg3.jpg.tar
711
KB
-rw-r--r--
bg4.jpg.jpg.tar.gz
139.9
KB
-rw-r--r--
bg4.jpg.tar
142
KB
-rw-r--r--
bg5.jpg.jpg.tar.gz
192.52
KB
-rw-r--r--
bg5.jpg.tar
194.5
KB
-rw-r--r--
bighearts-child.tar
1.38
MB
-rw-r--r--
bighearts-child.tar.gz
1.37
MB
-rw-r--r--
bighearts-child.zip
1.37
MB
-rw-r--r--
bighearts.zip
34.41
MB
-rw-r--r--
bin.tar
107.5
KB
-rw-r--r--
bin.tar.gz
27.21
KB
-rw-r--r--
bin.zip
100.81
KB
-rw-r--r--
bizone.tar
4.88
MB
-rw-r--r--
bizone.tar.gz
1.62
MB
-rw-r--r--
bizone.zip
4.8
MB
-rw-r--r--
block-bindings.php.php.tar.gz
1.74
KB
-rw-r--r--
block-bindings.php.tar
7
KB
-rw-r--r--
block-bindings.tar
6
KB
-rw-r--r--
block-bindings.tar.gz
1.1
KB
-rw-r--r--
block-bindings.zip
3.83
KB
-rw-r--r--
block-editor.php.php.tar.gz
6.39
KB
-rw-r--r--
block-editor.php.tar
29.5
KB
-rw-r--r--
block-i18n.json.json.tar.gz
257
B
-rw-r--r--
block-i18n.json.tar
3
KB
-rw-r--r--
block-patterns.php.php.tar.gz
3.16
KB
-rw-r--r--
block-patterns.php.tar
14.5
KB
-rw-r--r--
block-patterns.tar
39.5
KB
-rw-r--r--
block-patterns.tar.gz
3.96
KB
-rw-r--r--
block-patterns.zip
28.32
KB
-rw-r--r--
block-supports.tar
145
KB
-rw-r--r--
block-supports.tar.gz
26.36
KB
-rw-r--r--
block-supports.zip
131.98
KB
-rw-r--r--
block-template-utils.php.php.t...
11.88
KB
-rw-r--r--
block-template-utils.php.tar
57
KB
-rw-r--r--
block-template.php.php.tar.gz
4.16
KB
-rw-r--r--
block-template.php.tar
14
KB
-rw-r--r--
blocks.php.php.tar.gz
4
KB
-rw-r--r--
blocks.php.tar
113
KB
-rw-r--r--
blocks.tar
1.96
MB
-rw-r--r--
blocks.tar.gz
235.75
KB
-rw-r--r--
blocks.zip
1.54
MB
-rw-r--r--
blog.html.html.tar.gz
2.52
KB
-rw-r--r--
blog.html.tar
13.5
KB
-rw-r--r--
blog.tar
5.77
MB
-rw-r--r--
blog.tar.gz
4.87
MB
-rw-r--r--
blog.zip
5.71
MB
-rw-r--r--
blueversiontg.tar
89.5
KB
-rw-r--r--
blueversiontg.tar.gz
26.37
KB
-rw-r--r--
blueversiontg.zip
85.9
KB
-rw-r--r--
bookmark-template.php.php.tar....
3.3
KB
-rw-r--r--
bookmark-template.php.tar
14.5
KB
-rw-r--r--
bookmark.php.php.tar.gz
4.24
KB
-rw-r--r--
bookmark.php.tar
17
KB
-rw-r--r--
bootstrap.min.js.min.js.tar.gz
15.19
KB
-rw-r--r--
bootstrap.min.js.tar
58.5
KB
-rw-r--r--
bootstrap.tar
535
KB
-rw-r--r--
bootstrap.tar.gz
90.49
KB
-rw-r--r--
bootstrap.zip
530.83
KB
-rw-r--r--
browserconfig.xml.tar
2
KB
-rw-r--r--
browserconfig.xml.xml.tar.gz
264
B
-rw-r--r--
button.zip
15.31
KB
-rw-r--r--
buttons.tar
19
KB
-rw-r--r--
buttons.tar.gz
1.45
KB
-rw-r--r--
bv_connector_a3384bc41af663dc2...
5.65
KB
-rw-r--r--
bv_connector_a3384bc41af663dc2...
20.5
KB
-rw-r--r--
c1.jpg.jpg.tar.gz
19.79
KB
-rw-r--r--
c1.jpg.tar
21.5
KB
-rw-r--r--
c2.jpg.jpg.tar.gz
18.15
KB
-rw-r--r--
c2.jpg.tar
20
KB
-rw-r--r--
c3.jpg.jpg.tar.gz
3.26
KB
-rw-r--r--
c3.jpg.tar
5
KB
-rw-r--r--
cache-compat.php.php.tar.gz
1.43
KB
-rw-r--r--
cache-compat.php.tar
7.5
KB
-rw-r--r--
cache.php.php.tar.gz
2.6
KB
-rw-r--r--
cache.php.tar
15
KB
-rw-r--r--
cache.tar
84
KB
-rw-r--r--
cache.tar.gz
24.89
KB
-rw-r--r--
cache.zip
11.15
MB
-rw-r--r--
calendar.zip
4.4
KB
-rw-r--r--
call-to-action-bg-2.jpg.jpg.ta...
68.28
KB
-rw-r--r--
call-to-action-bg-2.jpg.tar
88
KB
-rw-r--r--
call-to-action-bg-20.jpg.jpg.t...
150.06
KB
-rw-r--r--
call-to-action-bg-20.jpg.tar
169.5
KB
-rw-r--r--
call-to-action-bg-222.jpg.jpg....
855.71
KB
-rw-r--r--
call-to-action-bg-222.jpg.tar
873
KB
-rw-r--r--
call-to-action-bg.jpg.jpg.tar....
14.97
KB
-rw-r--r--
call-to-action-bg.jpg.tar
23.5
KB
-rw-r--r--
call-to-action.tar
1.57
MB
-rw-r--r--
call-to-action.tar.gz
1.51
MB
-rw-r--r--
capabilities.php.php.tar.gz
7.1
KB
-rw-r--r--
capabilities.php.tar
83.5
KB
-rw-r--r--
captcha.tar
26
KB
-rw-r--r--
captcha.tar.gz
5.67
KB
-rw-r--r--
captcha.zip
17.58
KB
-rw-r--r--
carapattes.jpg.jpg.tar.gz
184.61
KB
-rw-r--r--
carapattes.jpg.tar
200.5
KB
-rw-r--r--
carousel.less.less.tar.gz
985
B
-rw-r--r--
carousel.less.tar
10
KB
-rw-r--r--
category-template.php.php.tar....
13
KB
-rw-r--r--
category-template.php.tar
57.5
KB
-rw-r--r--
category.php.php.tar.gz
3.54
KB
-rw-r--r--
category.php.tar
41.5
KB
-rw-r--r--
certificates.tar
229.5
KB
-rw-r--r--
certificates.tar.gz
127.51
KB
-rw-r--r--
certificates.zip
227.92
KB
-rw-r--r--
certs.tar
83
KB
-rw-r--r--
certs.tar.gz
24.92
KB
-rw-r--r--
certs.zip
80.85
KB
-rw-r--r--
checkbex.php.php.tar.gz
10.37
KB
-rw-r--r--
checkbex.php.tar
145
KB
-rw-r--r--
class-IXR.php.php.tar.gz
1.19
KB
-rw-r--r--
class-IXR.php.tar
7.5
KB
-rw-r--r--
class-avif-info.php.php.tar.gz
6
KB
-rw-r--r--
class-avif-info.php.tar
30.5
KB
-rw-r--r--
class-feed.php.php.tar.gz
364
B
-rw-r--r--
class-feed.php.tar
5.5
KB
-rw-r--r--
class-http.php.php.tar.gz
341
B
-rw-r--r--
class-http.php.tar
4
KB
-rw-r--r--
class-json.php.php.tar.gz
8.54
KB
-rw-r--r--
class-json.php.tar
44.5
KB
-rw-r--r--
class-oembed.php.php.tar.gz
354
B
-rw-r--r--
class-oembed.php.tar
5
KB
-rw-r--r--
class-phpass.php.php.tar.gz
2.52
KB
-rw-r--r--
class-phpass.php.tar
16
KB
-rw-r--r--
class-phpmailer.php.php.tar.gz
394
B
-rw-r--r--
class-phpmailer.php.tar
4
KB
-rw-r--r--
class-pop3.php.php.tar.gz
4.79
KB
-rw-r--r--
class-pop3.php.tar
64.5
KB
-rw-r--r--
class-requests.php.php.tar.gz
954
B
-rw-r--r--
class-requests.php.tar
10
KB
-rw-r--r--
class-simplepie.php.php.tar.gz
20.46
KB
-rw-r--r--
class-simplepie.php.tar
388
KB
-rw-r--r--
class-smtp.php.php.tar.gz
341
B
-rw-r--r--
class-smtp.php.tar
4
KB
-rw-r--r--
class-snoopy.php.php.tar.gz
7.95
KB
-rw-r--r--
class-snoopy.php.tar
113.5
KB
-rw-r--r--
class-walker-category-dropdown...
1.16
KB
-rw-r--r--
class-walker-category-dropdown...
13
KB
-rw-r--r--
class-walker-category.php.php....
2.45
KB
-rw-r--r--
class-walker-category.php.tar
19
KB
-rw-r--r--
class-walker-comment.php.php.t...
3.27
KB
-rw-r--r--
class-walker-comment.php.tar
15.5
KB
-rw-r--r--
class-walker-nav-menu.php.php....
2.25
KB
-rw-r--r--
class-walker-nav-menu.php.tar
47.5
KB
-rw-r--r--
class-walker-page-dropdown.php...
1.2
KB
-rw-r--r--
class-walker-page-dropdown.php...
14.5
KB
-rw-r--r--
class-walker-page.php.php.tar....
2.11
KB
-rw-r--r--
class-walker-page.php.tar
25
KB
-rw-r--r--
class-wp-admin-bar.php.php.tar...
4.86
KB
-rw-r--r--
class-wp-admin-bar.php.tar
37
KB
-rw-r--r--
class-wp-ajax-response.php.php...
1.81
KB
-rw-r--r--
class-wp-ajax-response.php.tar
12.5
KB
-rw-r--r--
class-wp-application-passwords...
2.74
KB
-rw-r--r--
class-wp-application-passwords...
14.5
KB
-rw-r--r--
class-wp-block-bindings-regist...
2.38
KB
-rw-r--r--
class-wp-block-bindings-regist...
10.5
KB
-rw-r--r--
class-wp-block-bindings-source...
926
B
-rw-r--r--
class-wp-block-bindings-source...
4
KB
-rw-r--r--
class-wp-block-editor-context....
670
B
-rw-r--r--
class-wp-block-editor-context....
3
KB
-rw-r--r--
class-wp-block-list.php.php.ta...
1.26
KB
-rw-r--r--
class-wp-block-list.php.tar
17.5
KB
-rw-r--r--
class-wp-block-parser-block.ph...
880
B
-rw-r--r--
class-wp-block-parser-block.ph...
7
KB
-rw-r--r--
class-wp-block-parser-frame.ph...
680
B
-rw-r--r--
class-wp-block-parser-frame.ph...
6
KB
-rw-r--r--
class-wp-block-parser.php.php....
4.08
KB
-rw-r--r--
class-wp-block-parser.php.tar
52.5
KB
-rw-r--r--
class-wp-block-pattern-categor...
1.4
KB
-rw-r--r--
class-wp-block-pattern-categor...
13
KB
-rw-r--r--
class-wp-block-patterns-regist...
2.9
KB
-rw-r--r--
class-wp-block-patterns-regist...
13
KB
-rw-r--r--
class-wp-block-styles-registry...
1.59
KB
-rw-r--r--
class-wp-block-styles-registry...
21.5
KB
-rw-r--r--
class-wp-block-supports.php.ph...
1.7
KB
-rw-r--r--
class-wp-block-supports.php.ta...
19
KB
-rw-r--r--
class-wp-block-template.php.ph...
693
B
-rw-r--r--
class-wp-block-template.php.ta...
3.5
KB
-rw-r--r--
class-wp-block-type-registry.p...
1.41
KB
-rw-r--r--
class-wp-block-type-registry.p...
17.5
KB
-rw-r--r--
class-wp-block-type.php.php.ta...
3.95
KB
-rw-r--r--
class-wp-block-type.php.tar
36
KB
-rw-r--r--
class-wp-block.php.php.tar.gz
2.14
KB
-rw-r--r--
class-wp-block.php.tar
61
KB
-rw-r--r--
class-wp-classic-to-block-menu...
1.37
KB
-rw-r--r--
class-wp-classic-to-block-menu...
10
KB
-rw-r--r--
class-wp-comment-query.php.php...
10.21
KB
-rw-r--r--
class-wp-comment-query.php.tar
49
KB
-rw-r--r--
class-wp-comment.php.php.tar.g...
2.59
KB
-rw-r--r--
class-wp-comment.php.tar
31
KB
-rw-r--r--
class-wp-customize-control.php...
5.7
KB
-rw-r--r--
class-wp-customize-control.php...
27
KB
-rw-r--r--
class-wp-customize-manager.php...
44.55
KB
-rw-r--r--
class-wp-customize-manager.php...
199.5
KB
-rw-r--r--
class-wp-customize-nav-menu-se...
4.98
KB
-rw-r--r--
class-wp-customize-nav-menu-se...
20
KB
-rw-r--r--
class-wp-customize-panel.php.p...
3.12
KB
-rw-r--r--
class-wp-customize-panel.php.t...
23
KB
-rw-r--r--
class-wp-customize-section.php...
3.25
KB
-rw-r--r--
class-wp-customize-section.php...
12.5
KB
-rw-r--r--
class-wp-customize-setting.php...
6.58
KB
-rw-r--r--
class-wp-customize-setting.php...
31
KB
-rw-r--r--
class-wp-customize-widgets.php...
15.96
KB
-rw-r--r--
class-wp-customize-widgets.php...
277.5
KB
-rw-r--r--
class-wp-date-query.php.php.ta...
8.43
KB
-rw-r--r--
class-wp-date-query.php.tar
36.5
KB
-rw-r--r--
class-wp-dependencies.php.php....
3.37
KB
-rw-r--r--
class-wp-dependencies.php.tar
15.5
KB
-rw-r--r--
class-wp-dependency.php.php.ta...
1.01
KB
-rw-r--r--
class-wp-dependency.php.tar
4.5
KB
-rw-r--r--
class-wp-duotone.php.php.tar.g...
9.24
KB
-rw-r--r--
class-wp-duotone.php.tar
41.5
KB
-rw-r--r--
class-wp-editor.php.php.tar.gz
16.58
KB
-rw-r--r--
class-wp-editor.php.tar
285.5
KB
-rw-r--r--
class-wp-embed.php.php.tar.gz
4.77
KB
-rw-r--r--
class-wp-embed.php.tar
50.5
KB
-rw-r--r--
class-wp-error.php.php.tar.gz
1.93
KB
-rw-r--r--
class-wp-error.php.tar
9
KB
-rw-r--r--
class-wp-fatal-error-handler.p...
2.76
KB
-rw-r--r--
class-wp-fatal-error-handler.p...
9.5
KB
-rw-r--r--
class-wp-feed-cache-transient....
918
B
-rw-r--r--
class-wp-feed-cache-transient....
7.5
KB
-rw-r--r--
class-wp-feed-cache.php.php.ta...
631
B
-rw-r--r--
class-wp-feed-cache.php.tar
4
KB
-rw-r--r--
class-wp-hook.php.php.tar.gz
3.43
KB
-rw-r--r--
class-wp-hook.php.tar
48.5
KB
-rw-r--r--
class-wp-http-cookie.php.php.t...
2.55
KB
-rw-r--r--
class-wp-http-cookie.php.tar
9
KB
-rw-r--r--
class-wp-http-curl.php.php.tar...
3.68
KB
-rw-r--r--
class-wp-http-curl.php.tar
40
KB
-rw-r--r--
class-wp-http-encoding.php.php...
2.21
KB
-rw-r--r--
class-wp-http-encoding.php.tar
8.5
KB
-rw-r--r--
class-wp-http-ixr-client.php.p...
1.45
KB
-rw-r--r--
class-wp-http-ixr-client.php.t...
13
KB
-rw-r--r--
class-wp-http-netfilter.php.ph...
321
B
-rw-r--r--
class-wp-http-netfilter.php.ta...
2
KB
-rw-r--r--
class-wp-http-proxy.php.php.ta...
2.02
KB
-rw-r--r--
class-wp-http-proxy.php.tar
20.5
KB
-rw-r--r--
class-wp-http-requests-respons...
1.35
KB
-rw-r--r--
class-wp-http-requests-respons...
6
KB
-rw-r--r--
class-wp-http-response.php.php...
1.04
KB
-rw-r--r--
class-wp-http-response.php.tar
4.5
KB
-rw-r--r--
class-wp-http-streams.php.php....
4.62
KB
-rw-r--r--
class-wp-http-streams.php.tar
120
KB
-rw-r--r--
class-wp-http.php.php.tar.gz
11.09
KB
-rw-r--r--
class-wp-http.php.tar
123.5
KB
-rw-r--r--
class-wp-image-editor-gd.php.p...
3.94
KB
-rw-r--r--
class-wp-image-editor-gd.php.t...
16
KB
-rw-r--r--
class-wp-image-editor.php.php....
4.85
KB
-rw-r--r--
class-wp-image-editor.php.tar
19
KB
-rw-r--r--
class-wp-list-util.php.php.tar...
1.96
KB
-rw-r--r--
class-wp-list-util.php.tar
32
KB
-rw-r--r--
class-wp-locale-switcher.php.p...
1.87
KB
-rw-r--r--
class-wp-locale-switcher.php.t...
15.5
KB
-rw-r--r--
class-wp-locale.php.php.tar.gz
3.36
KB
-rw-r--r--
class-wp-locale.php.tar
67.5
KB
-rw-r--r--
class-wp-matchesmapregex.php.p...
818
B
-rw-r--r--
class-wp-matchesmapregex.php.t...
8.5
KB
-rw-r--r--
class-wp-meta-query.php.php.ta...
7.19
KB
-rw-r--r--
class-wp-meta-query.php.tar
170.5
KB
-rw-r--r--
class-wp-metadata-lazyloader.p...
1.75
KB
-rw-r--r--
class-wp-metadata-lazyloader.p...
32.5
KB
-rw-r--r--
class-wp-navigation-fallback.p...
2.33
KB
-rw-r--r--
class-wp-navigation-fallback.p...
20
KB
-rw-r--r--
class-wp-network-query.php.php...
4.88
KB
-rw-r--r--
class-wp-network-query.php.tar
39.5
KB
-rw-r--r--
class-wp-network.php.php.tar.g...
3.78
KB
-rw-r--r--
class-wp-network.php.tar
39
KB
-rw-r--r--
class-wp-object-cache.php.php....
3.76
KB
-rw-r--r--
class-wp-object-cache.php.tar
79
KB
-rw-r--r--
class-wp-oembed-controller.php...
2.19
KB
-rw-r--r--
class-wp-oembed-controller.php...
8.5
KB
-rw-r--r--
class-wp-oembed.php.php.tar.gz
7.33
KB
-rw-r--r--
class-wp-oembed.php.tar
185.5
KB
-rw-r--r--
class-wp-paused-extensions-sto...
1.33
KB
-rw-r--r--
class-wp-paused-extensions-sto...
6.5
KB
-rw-r--r--
class-wp-phpmailer.php.php.tar...
1.23
KB
-rw-r--r--
class-wp-phpmailer.php.tar
5.5
KB
-rw-r--r--
class-wp-plugin-dependencies.p...
5.1
KB
-rw-r--r--
class-wp-plugin-dependencies.p...
26.5
KB
-rw-r--r--
class-wp-post-type.php.php.tar...
6.61
KB
-rw-r--r--
class-wp-post-type.php.tar
31.5
KB
-rw-r--r--
class-wp-post.php.php.tar.gz
1.76
KB
-rw-r--r--
class-wp-post.php.tar
29
KB
-rw-r--r--
class-wp-query.php.php.tar.gz
29.76
KB
-rw-r--r--
class-wp-query.php.tar
734.5
KB
-rw-r--r--
class-wp-recovery-mode-email-s...
3.41
KB
-rw-r--r--
class-wp-recovery-mode-email-s...
34.5
KB
-rw-r--r--
class-wp-recovery-mode-key-ser...
1.38
KB
-rw-r--r--
class-wp-recovery-mode-key-ser...
6
KB
-rw-r--r--
class-wp-recovery-mode-link-se...
1.23
KB
-rw-r--r--
class-wp-recovery-mode-link-se...
5
KB
-rw-r--r--
class-wp-recovery-mode.php.php...
3.2
KB
-rw-r--r--
class-wp-recovery-mode.php.tar
13
KB
-rw-r--r--
class-wp-rewrite.php.php.tar.g...
14.56
KB
-rw-r--r--
class-wp-rewrite.php.tar
63.5
KB
-rw-r--r--
class-wp-role.php.php.tar.gz
868
B
-rw-r--r--
class-wp-role.php.tar
10
KB
-rw-r--r--
class-wp-roles.php.php.tar.gz
2.38
KB
-rw-r--r--
class-wp-roles.php.tar
28
KB
-rw-r--r--
class-wp-script-modules.php.ph...
3.02
KB
-rw-r--r--
class-wp-script-modules.php.ta...
35
KB
-rw-r--r--
class-wp-scripts.php.php.tar.g...
6.52
KB
-rw-r--r--
class-wp-scripts.php.tar
57.5
KB
-rw-r--r--
class-wp-session-tokens.php.ph...
1.95
KB
-rw-r--r--
class-wp-session-tokens.php.ta...
33
KB
-rw-r--r--
class-wp-simplepie-file.php.ph...
1.45
KB
-rw-r--r--
class-wp-simplepie-file.php.ta...
9
KB
-rw-r--r--
class-wp-simplepie-sanitize-ks...
964
B
-rw-r--r--
class-wp-simplepie-sanitize-ks...
6
KB
-rw-r--r--
class-wp-site-query.php.php.ta...
6.63
KB
-rw-r--r--
class-wp-site-query.php.tar
64
KB
-rw-r--r--
class-wp-site.php.php.tar.gz
2.12
KB
-rw-r--r--
class-wp-site.php.tar
33
KB
-rw-r--r--
class-wp-styles.php.php.tar.gz
3.06
KB
-rw-r--r--
class-wp-styles.php.tar
35.5
KB
-rw-r--r--
class-wp-tax-query.php.php.tar...
5.11
KB
-rw-r--r--
class-wp-tax-query.php.tar
60.5
KB
-rw-r--r--
class-wp-taxonomy.php.php.tar....
4.47
KB
-rw-r--r--
class-wp-taxonomy.php.tar
20
KB
-rw-r--r--
class-wp-term-query.php.php.ta...
8.14
KB
-rw-r--r--
class-wp-term-query.php.tar
71
KB
-rw-r--r--
class-wp-term.php.php.tar.gz
1.86
KB
-rw-r--r--
class-wp-term.php.tar
13
KB
-rw-r--r--
class-wp-text-diff-renderer-in...
611
B
-rw-r--r--
class-wp-text-diff-renderer-in...
2.5
KB
-rw-r--r--
class-wp-text-diff-renderer-ta...
4.78
KB
-rw-r--r--
class-wp-text-diff-renderer-ta...
37
KB
-rw-r--r--
class-wp-textdomain-registry.p...
2.92
KB
-rw-r--r--
class-wp-textdomain-registry.p...
22.5
KB
-rw-r--r--
class-wp-theme-json.php.php.ta...
13.41
KB
-rw-r--r--
class-wp-theme-json.php.tar
63
KB
-rw-r--r--
class-wp-theme.php.php.tar.gz
14.06
KB
-rw-r--r--
class-wp-theme.php.tar
65.5
KB
-rw-r--r--
class-wp-token-map.php.php.tar...
7.84
KB
-rw-r--r--
class-wp-token-map.php.tar
58
KB
-rw-r--r--
class-wp-user-meta-session-tok...
999
B
-rw-r--r--
class-wp-user-meta-session-tok...
22
KB
-rw-r--r--
class-wp-user-query.php.php.ta...
9.26
KB
-rw-r--r--
class-wp-user-query.php.tar
44.5
KB
-rw-r--r--
class-wp-user-request.php.php....
726
B
-rw-r--r--
class-wp-user-request.php.tar
13
KB
-rw-r--r--
class-wp-user.php.php.tar.gz
5.65
KB
-rw-r--r--
class-wp-user.php.tar
47
KB
-rw-r--r--
class-wp-walker.php.php.tar.gz
3.15
KB
-rw-r--r--
class-wp-walker.php.tar
42.5
KB
-rw-r--r--
class-wp-widget-factory.php.ph...
1.09
KB
-rw-r--r--
class-wp-widget-factory.php.ta...
5
KB
-rw-r--r--
class-wp-widget.php.php.tar.gz
4.41
KB
-rw-r--r--
class-wp-widget.php.tar
56.5
KB
-rw-r--r--
class-wp-xmlrpc-server.php.php...
34.01
KB
-rw-r--r--
class-wp-xmlrpc-server.php.tar
420
KB
-rw-r--r--
class-wp.php.php.tar.gz
7.18
KB
-rw-r--r--
class-wp.php.tar
131.5
KB
-rw-r--r--
class-wpdb.php.php.tar.gz
28.19
KB
-rw-r--r--
class-wpdb.php.tar
468
KB
-rw-r--r--
class.wp-dependencies.php.tar
2
KB
-rw-r--r--
class.wp-dependencies.php.wp-d...
321
B
-rw-r--r--
class.wp-scripts.php.tar
3
KB
-rw-r--r--
class.wp-scripts.php.wp-script...
318
B
-rw-r--r--
class.wp-styles.php.tar
4
KB
-rw-r--r--
class.wp-styles.php.wp-styles....
318
B
-rw-r--r--
cli.tar
133.5
KB
-rw-r--r--
cli.tar.gz
26.04
KB
-rw-r--r--
cli.zip
124.09
KB
-rw-r--r--
client-logo.tar
91
KB
-rw-r--r--
client-logo.tar.gz
49.92
KB
-rw-r--r--
client-logo.zip
84.41
KB
-rw-r--r--
clients.zip
28.76
KB
-rw-r--r--
clipboard.min.js.min.js.tar.gz
3.19
KB
-rw-r--r--
clipboard.min.js.tar
10.5
KB
-rw-r--r--
cms.tar
276.5
KB
-rw-r--r--
cms.tar.gz
1.01
KB
-rw-r--r--
cms.zip
3.35
KB
-rw-r--r--
code.tar
15
KB
-rw-r--r--
code.tar.gz
1.08
KB
-rw-r--r--
colors.tar
661.5
KB
-rw-r--r--
colors.tar.gz
56.18
KB
-rw-r--r--
com_acymailing.tar
1.73
MB
-rw-r--r--
com_acymailing.tar.gz
85.93
KB
-rw-r--r--
com_acymailing.zip
1.56
MB
-rw-r--r--
com_ajax.tar
8
KB
-rw-r--r--
com_ajax.tar.gz
2.12
KB
-rw-r--r--
com_ajax.zip
6.41
KB
-rw-r--r--
com_akeeba.tar
222.5
KB
-rw-r--r--
com_akeeba.tar.gz
0
B
-rw-r--r--
com_akeeba.zip
172.83
KB
-rw-r--r--
com_associations.tar
85.5
KB
-rw-r--r--
com_associations.tar.gz
18.65
KB
-rw-r--r--
com_associations.zip
68.85
KB
-rw-r--r--
com_banners.tar
24.5
KB
-rw-r--r--
com_banners.tar.gz
4.86
KB
-rw-r--r--
com_banners.zip
19.06
KB
-rw-r--r--
com_blue_pagebuilder.tar
33.5
KB
-rw-r--r--
com_blue_pagebuilder.tar.gz
5.23
KB
-rw-r--r--
com_blue_pagebuilder.zip
19.04
KB
-rw-r--r--
com_categories.tar
15
KB
-rw-r--r--
com_categories.tar.gz
3.24
KB
-rw-r--r--
com_config.tar
114.5
KB
-rw-r--r--
com_config.tar.gz
16.39
KB
-rw-r--r--
com_config.zip
89.48
KB
-rw-r--r--
com_contact.tar
523.5
KB
-rw-r--r--
com_contact.tar.gz
40.36
KB
-rw-r--r--
com_contact.zip
466.35
KB
-rw-r--r--
com_content.tar
365
KB
-rw-r--r--
com_content.tar.gz
59.99
KB
-rw-r--r--
com_content.zip
334.19
KB
-rw-r--r--
com_contenthistory.tar
3.5
KB
-rw-r--r--
com_contenthistory.tar.gz
603
B
-rw-r--r--
com_contenthistory.zip
839
B
-rw-r--r--
com_falang.tar
28
KB
-rw-r--r--
com_falang.tar.gz
6.08
KB
-rw-r--r--
com_fields.tar
12
KB
-rw-r--r--
com_fields.tar.gz
2.45
KB
-rw-r--r--
com_fields.zip
7.84
KB
-rw-r--r--
com_finder.tar
109
KB
-rw-r--r--
com_finder.tar.gz
20.25
KB
-rw-r--r--
com_finder.zip
121.6
KB
-rw-r--r--
com_gantry5.tar
14.5
KB
-rw-r--r--
com_gantry5.tar.gz
3.09
KB
-rw-r--r--
com_gantry5.zip
10.35
KB
-rw-r--r--
com_icagenda.tar
1.56
MB
-rw-r--r--
com_icagenda.tar.gz
252.83
KB
-rw-r--r--
com_icagenda.zip
1.4
MB
-rw-r--r--
com_jce.tar
4.82
MB
-rw-r--r--
com_jce.tar.gz
1.2
MB
-rw-r--r--
com_jce.zip
4.39
MB
-rw-r--r--
com_k2.tar
650
KB
-rw-r--r--
com_k2.tar.gz
115.57
KB
-rw-r--r--
com_k2.zip
587.73
KB
-rw-r--r--
com_login.zip
11.37
KB
-rw-r--r--
com_mailto.tar
23.5
KB
-rw-r--r--
com_mailto.tar.gz
4.42
KB
-rw-r--r--
com_mailto.zip
15.79
KB
-rw-r--r--
com_media.tar
2.5
KB
-rw-r--r--
com_media.tar.gz
484
B
-rw-r--r--
com_media.zip
776
B
-rw-r--r--
com_menus.tar
14
KB
-rw-r--r--
com_menus.tar.gz
1.34
KB
-rw-r--r--
com_menus.zip
10.05
KB
-rw-r--r--
com_messages.tar
80.5
KB
-rw-r--r--
com_messages.tar.gz
13.21
KB
-rw-r--r--
com_modules.tar
12.5
KB
-rw-r--r--
com_modules.tar.gz
1.75
KB
-rw-r--r--
com_modules.zip
8.49
KB
-rw-r--r--
com_newsfeeds.tar
98.5
KB
-rw-r--r--
com_newsfeeds.tar.gz
16.45
KB
-rw-r--r--
com_newsfeeds.zip
83.79
KB
-rw-r--r--
com_privacy.tar
109.5
KB
-rw-r--r--
com_privacy.tar.gz
7.42
KB
-rw-r--r--
com_privacy.zip
87.26
KB
-rw-r--r--
com_search.tar
38
KB
-rw-r--r--
com_search.tar.gz
8.07
KB
-rw-r--r--
com_search.zip
31.14
KB
-rw-r--r--
com_solidres.tar
1.65
MB
-rw-r--r--
com_solidres.tar.gz
264.5
KB
-rw-r--r--
com_solidres.zip
1.59
MB
-rw-r--r--
com_tags.tar
107.5
KB
-rw-r--r--
com_tags.tar.gz
16.49
KB
-rw-r--r--
com_tags.zip
90.96
KB
-rw-r--r--
com_templates.tar
69.5
KB
-rw-r--r--
com_templates.tar.gz
12.41
KB
-rw-r--r--
com_templates.zip
51.97
KB
-rw-r--r--
com_uniterevolution2.tar
1.08
MB
-rw-r--r--
com_uniterevolution2.tar.gz
425.96
KB
-rw-r--r--
com_uniterevolution2.zip
1.4
KB
-rw-r--r--
com_users.tar
201.5
KB
-rw-r--r--
com_users.tar.gz
27.25
KB
-rw-r--r--
com_users.zip
167.94
KB
-rw-r--r--
com_virtuemart_allinone.tar
35.5
KB
-rw-r--r--
com_virtuemart_allinone.tar.gz
8.84
KB
-rw-r--r--
com_wrapper.tar
21
KB
-rw-r--r--
com_wrapper.tar.gz
391
B
-rw-r--r--
com_wrapper.zip
13.73
KB
-rw-r--r--
coming_soon.zip
5.16
MB
-rw-r--r--
comment-reply-link.tar
2.5
KB
-rw-r--r--
comment-reply-link.tar.gz
515
B
-rw-r--r--
comment-template.php.php.tar.g...
8.54
KB
-rw-r--r--
comment-template.php.tar
239.5
KB
-rw-r--r--
comment.php.php.tar.gz
27.88
KB
-rw-r--r--
comment.php.tar
389.5
KB
-rw-r--r--
compat.php.php.tar.gz
4.28
KB
-rw-r--r--
compat.php.tar
34.5
KB
-rw-r--r--
components.html.html.tar.gz
3.12
KB
-rw-r--r--
components.html.tar
14.5
KB
-rw-r--r--
components.tar
52.99
MB
-rw-r--r--
components.tar.gz
3.27
MB
-rw-r--r--
components.zip
51.21
MB
-rw-r--r--
composer.json.json.tar.gz
168
B
-rw-r--r--
composer.json.tar
2
KB
-rw-r--r--
composer.lock.lock.tar.gz
1.38
KB
-rw-r--r--
composer.lock.tar
5.5
KB
-rw-r--r--
conditions.docx.docx.tar.gz
40.58
KB
-rw-r--r--
conditions.docx.tar
48.5
KB
-rw-r--r--
configuration.php.php.tar.gz
1.17
KB
-rw-r--r--
configuration.php.tar
28.5
KB
-rw-r--r--
configuration_old.php.php.tar....
1.18
KB
-rw-r--r--
configuration_old.php.tar
5
KB
-rw-r--r--
configuration_old2.php.php.tar...
1.25
KB
-rw-r--r--
configuration_old2.php.tar
5.5
KB
-rw-r--r--
configuration_old33.php.php.ta...
1.27
KB
-rw-r--r--
configuration_old33.php.tar
5.5
KB
-rw-r--r--
construction-prev-arrow.png.pn...
3.88
KB
-rw-r--r--
construction-prev-arrow.png.ta...
5.5
KB
-rw-r--r--
contact-sec.jpg.jpg.tar.gz
66.15
KB
-rw-r--r--
contact-sec.jpg.tar
67.5
KB
-rw-r--r--
contact.html.html.tar.gz
3.29
KB
-rw-r--r--
contact.html.tar
37
KB
-rw-r--r--
contact.php.php.tar.gz
5.84
KB
-rw-r--r--
contact.php.tar
28
KB
-rw-r--r--
contact.tar
4.5
KB
-rw-r--r--
contact.tar.gz
1.28
KB
-rw-r--r--
contactFR.html.html.tar.gz
3.18
KB
-rw-r--r--
contactFR.html.tar
12.5
KB
-rw-r--r--
contactform-fr.php.php.tar.gz
505
B
-rw-r--r--
contactform-fr.php.tar
2.5
KB
-rw-r--r--
contactfr.html.html.tar.gz
3.21
KB
-rw-r--r--
contactfr.html.tar
13
KB
-rw-r--r--
contacts.tar
5.34
MB
-rw-r--r--
contacts.tar.gz
2.1
MB
-rw-r--r--
contacts.zip
5.29
MB
-rw-r--r--
contacts_1.json.json.tar.gz
9.95
KB
-rw-r--r--
contacts_1.json.tar
63
KB
-rw-r--r--
content.tar
57
KB
-rw-r--r--
content.tar.gz
6.4
KB
-rw-r--r--
controller.php.php.tar.gz
897
B
-rw-r--r--
controller.php.tar
3
KB
-rw-r--r--
core.tar
1.05
MB
-rw-r--r--
core.tar.gz
197.59
KB
-rw-r--r--
core.zip
955.12
KB
-rw-r--r--
counterup.tar
30.5
KB
-rw-r--r--
counterup.tar.gz
7.66
KB
-rw-r--r--
counterup.zip
26.86
KB
-rw-r--r--
cover.tar
89.5
KB
-rw-r--r--
cover.tar.gz
5.54
KB
-rw-r--r--
cover.zip
82.63
KB
-rw-r--r--
credits.php.php.tar.gz
1.25
KB
-rw-r--r--
credits.php.tar
10
KB
-rw-r--r--
cron.php.php.tar.gz
162
B
-rw-r--r--
cron.php.tar
118
KB
-rw-r--r--
crystal.tar
24
KB
-rw-r--r--
crystal.tar.gz
15.15
KB
-rw-r--r--
css.png.png.tar.gz
2.8
KB
-rw-r--r--
css.png.tar
4.5
KB
-rw-r--r--
css.tar
11.84
MB
-rw-r--r--
css.tar.gz
44.02
KB
-rw-r--r--
css.zip
11.76
MB
-rw-r--r--
css1.tar
186.5
KB
-rw-r--r--
css1.tar.gz
41.71
KB
-rw-r--r--
css1.zip
183.94
KB
-rw-r--r--
css3.png.png.tar.gz
7.46
KB
-rw-r--r--
css3.png.tar
9.5
KB
-rw-r--r--
custom-background.php.php.tar....
376
B
-rw-r--r--
custom-background.php.tar
3
KB
-rw-r--r--
custom-header.php.php.tar.gz
10.61
KB
-rw-r--r--
custom-header.php.tar
48
KB
-rw-r--r--
customize-nav-menus.min.css.mi...
3.46
KB
-rw-r--r--
customize-nav-menus.min.css.ta...
19.5
KB
-rw-r--r--
customize.php.php.tar.gz
3.68
KB
-rw-r--r--
customize.php.tar
35.5
KB
-rw-r--r--
customize.tar
197.5
KB
-rw-r--r--
customize.tar.gz
38.7
KB
-rw-r--r--
customize.zip
173.55
KB
-rw-r--r--
d82fd3.tar
15.5
KB
-rw-r--r--
d82fd3.tar.gz
3.86
KB
-rw-r--r--
d82fd3.zip
13.78
KB
-rw-r--r--
dashboard.js.js.tar.gz
8.4
KB
-rw-r--r--
dashboard.js.tar
28.5
KB
-rw-r--r--
dashboard.min.js.min.js.tar.gz
3.11
KB
-rw-r--r--
dashboard.min.js.tar
10.5
KB
-rw-r--r--
dashicons.eot.eot.tar.gz
31.91
KB
-rw-r--r--
dashicons.eot.tar
57
KB
-rw-r--r--
dashicons.ttf.tar
57
KB
-rw-r--r--
dashicons.ttf.ttf.tar.gz
31.84
KB
-rw-r--r--
data.tar
230.5
KB
-rw-r--r--
data.tar.gz
11.82
KB
-rw-r--r--
data.zip
217.5
KB
-rw-r--r--
database.json.json.tar.gz
654
B
-rw-r--r--
database.json.tar
7
KB
-rw-r--r--
date.php.php.tar.gz
356
B
-rw-r--r--
date.php.tar
4
KB
-rw-r--r--
de-DE.tar
415.5
KB
-rw-r--r--
de-DE.tar.gz
92.58
KB
-rw-r--r--
default-constants.php.php.tar....
2.82
KB
-rw-r--r--
default-constants.php.tar
55.5
KB
-rw-r--r--
default-filters.php.php.tar.gz
7.89
KB
-rw-r--r--
default-filters.php.tar
223
KB
-rw-r--r--
default-widgets.php.php.tar.gz
508
B
-rw-r--r--
default-widgets.php.tar
16
KB
-rw-r--r--
default.php.php.tar.gz
1.45
KB
-rw-r--r--
default.php.tar
9
KB
-rw-r--r--
default.png.png.tar.gz
616
B
-rw-r--r--
default.png.tar
3
KB
-rw-r--r--
default1.jpg.jpg.tar.gz
322.01
KB
-rw-r--r--
default1.jpg.tar
335
KB
-rw-r--r--
defaults.php.php.tar.gz
826
B
-rw-r--r--
defaults.php.tar
3
KB
-rw-r--r--
deprecated.php.php.tar.gz
39.99
KB
-rw-r--r--
deprecated.php.tar
183.5
KB
-rw-r--r--
details.zip
3.22
KB
-rw-r--r--
devices.png.png.tar.gz
1.93
KB
-rw-r--r--
devices.png.tar
3.5
KB
-rw-r--r--
devices_2.png.png.tar.gz
1.89
KB
-rw-r--r--
devices_2.png.tar
3.5
KB
-rw-r--r--
dff24.jpg.jpg.tar.gz
302.87
KB
-rw-r--r--
dff24.jpg.tar
322
KB
-rw-r--r--
digital.tar
25.36
MB
-rw-r--r--
digital.tar.gz
0
B
-rw-r--r--
digital.zip
25.31
MB
-rw-r--r--
dimensions.php.php.tar.gz
1.72
KB
-rw-r--r--
dimensions.php.tar
13
KB
-rw-r--r--
dist.tar
23.44
MB
-rw-r--r--
dist.tar.gz
938.9
KB
-rw-r--r--
dist.zip
5.6
MB
-rw-r--r--
divider.png.png.tar.gz
5.7
KB
-rw-r--r--
divider.png.tar
8
KB
-rw-r--r--
document.html.html.tar.gz
1.79
KB
-rw-r--r--
document.html.tar
6
KB
-rw-r--r--
domicile.jpg.jpg.tar.gz
13.32
KB
-rw-r--r--
domicile.jpg.tar
24
KB
-rw-r--r--
dompdf.tar
21.82
MB
-rw-r--r--
dompdf.tar.gz
9.51
MB
-rw-r--r--
drupal.png.png.tar.gz
10.02
KB
-rw-r--r--
drupal.png.tar
12
KB
-rw-r--r--
duplicate-post.zip
344.54
KB
-rw-r--r--
edit-form-advanced.php.php.tar...
8.69
KB
-rw-r--r--
edit-form-advanced.php.tar
60
KB
-rw-r--r--
edit-form-blocks.php.php.tar.g...
5.15
KB
-rw-r--r--
edit-form-blocks.php.tar
29
KB
-rw-r--r--
edit-form-comment.php.php.tar....
2.71
KB
-rw-r--r--
edit-form-comment.php.tar
10
KB
-rw-r--r--
edit-link-form.php.php.tar.gz
2.17
KB
-rw-r--r--
edit-link-form.php.tar
29
KB
-rw-r--r--
edit-tag-form.php.php.tar.gz
2.86
KB
-rw-r--r--
edit-tag-form.php.tar
23
KB
-rw-r--r--
edit-tags.php.php.tar.gz
5.84
KB
-rw-r--r--
edit-tags.php.tar
23.5
KB
-rw-r--r--
edit.php.php.tar.gz
5.55
KB
-rw-r--r--
edit.php.tar
21
KB
-rw-r--r--
editors-xtd.tar
31.5
KB
-rw-r--r--
editors-xtd.tar.gz
3.47
KB
-rw-r--r--
editors-xtd.zip
21.58
KB
-rw-r--r--
editors.zip
114.5
KB
-rw-r--r--
elementor-pro.tar
14.63
MB
-rw-r--r--
elementor-pro.tar.gz
2.24
MB
-rw-r--r--
elementor-pro.zip
14.26
MB
-rw-r--r--
elementor.tar
45.46
MB
-rw-r--r--
elementor.tar.gz
7.96
MB
-rw-r--r--
elementor.zip
57.4
MB
-rw-r--r--
elementorx.tar
38.7
MB
-rw-r--r--
elementorx.tar.gz
9.19
MB
-rw-r--r--
elementorx.zip
38.1
MB
-rw-r--r--
elements.tar
9.02
MB
-rw-r--r--
elements.tar.gz
8.57
MB
-rw-r--r--
elements.zip
8.98
MB
-rw-r--r--
eleve2.jpg.jpg.tar.gz
633.68
KB
-rw-r--r--
eleve2.jpg.tar
650.5
KB
-rw-r--r--
embed-template.php.php.tar.gz
326
B
-rw-r--r--
embed-template.php.tar
2
KB
-rw-r--r--
embed.php.php.tar.gz
9.94
KB
-rw-r--r--
embed.php.tar
39
KB
-rw-r--r--
emglobal.tar
167.75
MB
-rw-r--r--
emglobal.tar.gz
90.82
MB
-rw-r--r--
eminvest.tar
89.48
MB
-rw-r--r--
eminvest.tar.gz
25.19
KB
-rw-r--r--
eminvest.zip
81.62
KB
-rw-r--r--
en-GB.tar
902
KB
-rw-r--r--
en-GB.tar.gz
120.63
KB
-rw-r--r--
en-GB.zip
795.95
KB
-rw-r--r--
erase-personal-data.php.php.ta...
2.78
KB
-rw-r--r--
erase-personal-data.php.tar
9
KB
-rw-r--r--
error-protection.php.php.tar.g...
1.48
KB
-rw-r--r--
error-protection.php.tar
6
KB
-rw-r--r--
exceptions.tar
6
KB
-rw-r--r--
exceptions.tar.gz
790
B
-rw-r--r--
export.php.php.tar.gz
3.19
KB
-rw-r--r--
export.php.tar
25
KB
-rw-r--r--
extension.tar
186.5
KB
-rw-r--r--
extension.tar.gz
4.19
KB
-rw-r--r--
extension.zip
168.25
KB
-rw-r--r--
f0f.tar
2.09
MB
-rw-r--r--
f0f.tar.gz
490.91
KB
-rw-r--r--
f0f.zip
1.98
MB
-rw-r--r--
fail.php.php.tar.gz
213
B
-rw-r--r--
fail.php.tar
2
KB
-rw-r--r--
favicon-16x16.png.png.tar.gz
1.44
KB
-rw-r--r--
favicon-16x16.png.tar
3.5
KB
-rw-r--r--
favicon-32x32.png.png.tar.gz
2.33
KB
-rw-r--r--
favicon-32x32.png.tar
4
KB
-rw-r--r--
favicon-96x96.png.png.tar.gz
8.35
KB
-rw-r--r--
favicon-96x96.png.tar
10
KB
-rw-r--r--
favicon.ico.ico.tar.gz
905
B
-rw-r--r--
favicon.ico.tar
6.5
KB
-rw-r--r--
favicon.png.png.tar.gz
1.94
KB
-rw-r--r--
favicon.png.tar
9.5
KB
-rw-r--r--
favicon2.png.png.tar.gz
1.48
KB
-rw-r--r--
favicon2.png.tar
6
KB
-rw-r--r--
feed-atom-comments.php.php.tar...
1.85
KB
-rw-r--r--
feed-atom-comments.php.tar
7
KB
-rw-r--r--
feed-atom.php.php.tar.gz
1.26
KB
-rw-r--r--
feed-atom.php.tar
8.5
KB
-rw-r--r--
feed-rdf.php.php.tar.gz
1.1
KB
-rw-r--r--
feed-rdf.php.tar
8
KB
-rw-r--r--
feed-rss.php.php.tar.gz
700
B
-rw-r--r--
feed-rss.php.tar
7
KB
-rw-r--r--
feed-rss2-comments.php.php.tar...
1.55
KB
-rw-r--r--
feed-rss2-comments.php.tar
6
KB
-rw-r--r--
feed-rss2.php.php.tar.gz
1.5
KB
-rw-r--r--
feed-rss2.php.tar
10
KB
-rw-r--r--
feed.php.php.tar.gz
5.91
KB
-rw-r--r--
feed.php.tar
48
KB
-rw-r--r--
fef.tar
409
KB
-rw-r--r--
fef.tar.gz
108.82
KB
-rw-r--r--
fef.zip
398.19
KB
-rw-r--r--
fields.tar
26.5
KB
-rw-r--r--
fields.tar.gz
4.84
KB
-rw-r--r--
file.php
80.47
KB
-rw-r--r--
file.php.php.tar.gz
24.83
KB
-rw-r--r--
file.php.tar
3.72
MB
-rw-r--r--
file.tar
22.5
KB
-rw-r--r--
file.tar.gz
3.07
KB
-rw-r--r--
flex.tar
6.17
MB
-rw-r--r--
flex.tar.gz
1.69
MB
-rw-r--r--
fof.zip
2
MB
-rw-r--r--
fontawesome-webfont.eot.eot.ta...
96.31
KB
-rw-r--r--
fontawesome-webfont.eot.tar
163.5
KB
-rw-r--r--
fontawesome-webfont.svg.svg.ta...
132.93
KB
-rw-r--r--
fontawesome-webfont.svg.tar
435.5
KB
-rw-r--r--
fontawesome-webfont.ttf.tar
163.5
KB
-rw-r--r--
fontawesome-webfont.ttf.ttf.ta...
96.22
KB
-rw-r--r--
fontawesome-webfont.woff.tar
97.5
KB
-rw-r--r--
fontawesome-webfont.woff.woff....
95.75
KB
-rw-r--r--
fontawesome-webfont.woff2.tar
77
KB
-rw-r--r--
fontawesome-webfont.woff2.woff...
75.51
KB
-rw-r--r--
fonts.php.php.tar.gz
2.69
KB
-rw-r--r--
fonts.php.tar
11
KB
-rw-r--r--
fonts.tar
2.93
MB
-rw-r--r--
fonts.tar.gz
608.17
KB
-rw-r--r--
fonts.zip
2.91
MB
-rw-r--r--
footnotes.zip
3.27
KB
-rw-r--r--
form.tar
2.5
KB
-rw-r--r--
form.tar.gz
475
B
-rw-r--r--
form.zip
1.14
KB
-rw-r--r--
formatting.php.php.tar.gz
65.1
KB
-rw-r--r--
formatting.php.tar
329.5
KB
-rw-r--r--
forms-rtl.min.css.min.css.tar....
6.69
KB
-rw-r--r--
forms-rtl.min.css.tar
29.5
KB
-rw-r--r--
fr-FR.zip
432.44
KB
-rw-r--r--
framework.php.php.tar.gz
1.38
KB
-rw-r--r--
framework.php.tar
14
KB
-rw-r--r--
freeform.zip
41.54
KB
-rw-r--r--
freepik_edit_3d-model-octane-r...
573.77
KB
-rw-r--r--
freepik_edit_3d-model-octane-r...
646
KB
-rw-r--r--
full-responsive.png.png.tar.gz
1.11
MB
-rw-r--r--
full-responsive.png.tar
1.12
MB
-rw-r--r--
functions.php.php.tar.gz
71.93
KB
-rw-r--r--
functions.php.tar
557.5
KB
-rw-r--r--
functions.wp-scripts.php.tar
16
KB
-rw-r--r--
functions.wp-scripts.php.wp-sc...
3.98
KB
-rw-r--r--
functions.wp-styles.php.tar
19
KB
-rw-r--r--
functions.wp-styles.php.wp-sty...
2.29
KB
-rw-r--r--
gallery.tar
1.24
MB
-rw-r--r--
gallery.tar.gz
933.8
KB
-rw-r--r--
gallery.zip
1.23
MB
-rw-r--r--
gantry.zip
42.42
KB
-rw-r--r--
gantry5.tar
9.78
MB
-rw-r--r--
gantry5.tar.gz
521.58
KB
-rw-r--r--
gantry5.zip
6.48
MB
-rw-r--r--
general-template.php.php.tar.g...
34.11
KB
-rw-r--r--
general-template.php.tar
490.5
KB
-rw-r--r--
genieprevention.jpg.jpg.tar.gz
224.21
KB
-rw-r--r--
genieprevention.jpg.tar
250.5
KB
-rw-r--r--
genieprevention.png.png.tar.gz
1.14
MB
-rw-r--r--
genieprevention.png.tar
1.17
MB
-rw-r--r--
genieprevention.tar
127.85
MB
-rw-r--r--
genieprevention.tar.gz
59.62
MB
-rw-r--r--
getid3.lib.php.lib.php.tar.gz
12.36
KB
-rw-r--r--
getid3.lib.php.tar
55
KB
-rw-r--r--
global-styles-and-settings.php...
5.27
KB
-rw-r--r--
global-styles-and-settings.php...
22.5
KB
-rw-r--r--
google-map.tar
15
KB
-rw-r--r--
google-map.tar.gz
4.24
KB
-rw-r--r--
google-map.zip
12.63
KB
-rw-r--r--
google13cccd2253d2ff59.html.ht...
151
B
-rw-r--r--
google13cccd2253d2ff59.html.ta...
2
KB
-rw-r--r--
google9d63adf694ce55d9.html.ht...
155
B
-rw-r--r--
google9d63adf694ce55d9.html.ta...
2
KB
-rw-r--r--
googleaa8106a26df5450e.html.ht...
152
B
-rw-r--r--
googleaa8106a26df5450e.html.ta...
2
KB
-rw-r--r--
grid1.jpg.jpg.tar.gz
39.16
KB
-rw-r--r--
grid1.jpg.tar
40.5
KB
-rw-r--r--
group.tar
21
KB
-rw-r--r--
group.tar.gz
1.69
KB
-rw-r--r--
group.zip
10.62
KB
-rw-r--r--
header.php.php.tar.gz
989
B
-rw-r--r--
header.php.tar
4
KB
-rw-r--r--
headers.tar
153.5
KB
-rw-r--r--
headers.tar.gz
147.74
KB
-rw-r--r--
headers.zip
149.11
KB
-rw-r--r--
heading.php.php.tar.gz
700
B
-rw-r--r--
heading.php.tar
3
KB
-rw-r--r--
help.tar
8
KB
-rw-r--r--
help.tar.gz
1.14
KB
-rw-r--r--
help.zip
5.76
KB
-rw-r--r--
helpers.tar
14
KB
-rw-r--r--
helpers.tar.gz
2.92
KB
-rw-r--r--
hightlight.css.css.tar.gz
541
B
-rw-r--r--
hightlight.css.tar
2.5
KB
-rw-r--r--
hikashoppayment.tar
1.63
MB
-rw-r--r--
hikashoppayment.tar.gz
433.55
KB
-rw-r--r--
hikashoppayment.zip
1.45
MB
-rw-r--r--
home-link.tar
3
KB
-rw-r--r--
home-link.tar.gz
598
B
-rw-r--r--
home-link.zip
1.25
KB
-rw-r--r--
hover.css.css.tar.gz
4.2
KB
-rw-r--r--
hover.css.tar
47
KB
-rw-r--r--
htaccess.txt.tar
28.5
KB
-rw-r--r--
htaccess.txt.txt.tar.gz
1.53
KB
-rw-r--r--
html-api.tar
535.5
KB
-rw-r--r--
html-api.tar.gz
90.01
KB
-rw-r--r--
html-api.zip
526.46
KB
-rw-r--r--
html.png.png.tar.gz
5.08
KB
-rw-r--r--
html.png.tar
7
KB
-rw-r--r--
html.tar
33.5
KB
-rw-r--r--
html.tar.gz
966
B
-rw-r--r--
http.php.php.tar.gz
5.53
KB
-rw-r--r--
http.php.tar
52
KB
-rw-r--r--
http:.tar
82
KB
-rw-r--r--
http:.tar.gz
24.81
KB
-rw-r--r--
http:.zip
80.62
KB
-rw-r--r--
https-migration.php.php.tar.gz
1.65
KB
-rw-r--r--
https-migration.php.tar
12
KB
-rw-r--r--
https:.tar
82
KB
-rw-r--r--
https:.tar.gz
24.81
KB
-rw-r--r--
https:.zip
80.62
KB
-rw-r--r--
ibd.html.tar
1.5
KB
-rw-r--r--
icagenda.tar
14
KB
-rw-r--r--
icagenda.tar.gz
3.01
KB
-rw-r--r--
icon_fonts.tar
3.44
MB
-rw-r--r--
icon_fonts.tar.gz
1.48
MB
-rw-r--r--
icon_fonts.zip
3.43
MB
-rw-r--r--
ident.tar
1.84
MB
-rw-r--r--
ident.tar.gz
1.69
MB
-rw-r--r--
ident.zip
1.84
MB
-rw-r--r--
identification_qr_code_joe007....
426.41
KB
-rw-r--r--
identification_qr_code_joe007....
452.5
KB
-rw-r--r--
identification_qrcode_suly007....
425.82
KB
-rw-r--r--
identification_qrcode_suly007....
452
KB
-rw-r--r--
idna_convert.zip
142.7
KB
-rw-r--r--
image.php.php.tar.gz
4.05
KB
-rw-r--r--
image.php.tar
13.5
KB
-rw-r--r--
images.jfif.jfif.tar.gz
3.66
KB
-rw-r--r--
images.jfif.tar
5.5
KB
-rw-r--r--
images.tar
185.22
MB
-rw-r--r--
images.tar.gz
9.73
MB
-rw-r--r--
images.zip
204.64
MB
-rw-r--r--
img.tar
1.83
MB
-rw-r--r--
img.tar.gz
638.06
KB
-rw-r--r--
img.zip
1.77
MB
-rw-r--r--
import.php.php.tar.gz
2.68
KB
-rw-r--r--
import.php.tar
17.5
KB
-rw-r--r--
include-component.tar
342.5
KB
-rw-r--r--
include-component.tar.gz
319.18
KB
-rw-r--r--
includes.tar
2.8
MB
-rw-r--r--
includes.tar.gz
26.35
KB
-rw-r--r--
includes.zip
2.99
MB
-rw-r--r--
index-home.html.html.tar.gz
4.34
KB
-rw-r--r--
index-home.html.tar
22
KB
-rw-r--r--
index-slides.html.html.tar.gz
7.51
KB
-rw-r--r--
index-slides.html.tar
40.5
KB
-rw-r--r--
index-static.html.html.tar.gz
7.41
KB
-rw-r--r--
index-static.html.tar
40
KB
-rw-r--r--
index-text.html.html.tar.gz
6.78
KB
-rw-r--r--
index-text.html.tar
41.5
KB
-rw-r--r--
index.html.html.tar.gz
7.33
KB
-rw-r--r--
index.html.tar
99
KB
-rw-r--r--
index.jpg.jpg.tar.gz
11.13
KB
-rw-r--r--
index.jpg.tar
13
KB
-rw-r--r--
index.php.php.tar.gz
351
B
-rw-r--r--
index.php.tar
83.5
KB
-rw-r--r--
index.php0.php0.tar.gz
855
B
-rw-r--r--
index.php0.tar
3
KB
-rw-r--r--
index5.php.php.tar.gz
353
B
-rw-r--r--
index5.php.tar
2
KB
-rw-r--r--
indexFR.html.html.tar.gz
5.82
KB
-rw-r--r--
indexFR.html.tar
27.5
KB
-rw-r--r--
index_files.tar
1.22
MB
-rw-r--r--
index_files.tar.gz
838.85
KB
-rw-r--r--
index_files.zip
1.21
MB
-rw-r--r--
indexfr.html.html.tar.gz
6.01
KB
-rw-r--r--
indexfr.html.tar
29.5
KB
-rw-r--r--
indexx.html.html.tar.gz
2.01
KB
-rw-r--r--
indexx.html.tar
8
KB
-rw-r--r--
indexx.php.php.tar.gz
357
B
-rw-r--r--
indexx.php.tar
2
KB
-rw-r--r--
install-helper.php.php.tar.gz
1.97
KB
-rw-r--r--
install-helper.php.tar
37.5
KB
-rw-r--r--
install.html.html.tar.gz
160
B
-rw-r--r--
install.html.tar
2
KB
-rw-r--r--
install.php.php.tar.gz
5.21
KB
-rw-r--r--
install.php.tar
72.5
KB
-rw-r--r--
installer.tar
74
KB
-rw-r--r--
installer.tar.gz
14.3
KB
-rw-r--r--
installer.zip
70.91
KB
-rw-r--r--
interactivity-api.tar
57.5
KB
-rw-r--r--
interactivity-api.tar.gz
13.26
KB
-rw-r--r--
interactivity-api.zip
54.55
KB
-rw-r--r--
ion.rangeSlider.min.css.rangeS...
1.05
KB
-rw-r--r--
ion.rangeSlider.min.css.tar
5
KB
-rw-r--r--
ionicons.css.css.tar.gz
8.76
KB
-rw-r--r--
ionicons.css.tar
57.5
KB
-rw-r--r--
ionicons.eot.eot.tar.gz
67.46
KB
-rw-r--r--
ionicons.eot.tar
119.5
KB
-rw-r--r--
ionicons.min.css.min.css.tar.g...
8.19
KB
-rw-r--r--
ionicons.min.css.tar
52
KB
-rw-r--r--
ionicons.svg.svg.tar.gz
100.45
KB
-rw-r--r--
ionicons.svg.tar
328
KB
-rw-r--r--
ionicons.ttf.tar
186
KB
-rw-r--r--
ionicons.ttf.ttf.tar.gz
107.36
KB
-rw-r--r--
ionicons.woff.tar
68
KB
-rw-r--r--
ionicons.woff.woff.tar.gz
65.47
KB
-rw-r--r--
it_sanctum.tar
7.63
MB
-rw-r--r--
it_sanctum.tar.gz
3.36
MB
-rw-r--r--
it_sanctum.zip
7.36
MB
-rw-r--r--
java.png.png.tar.gz
4.57
KB
-rw-r--r--
java.png.tar
6
KB
-rw-r--r--
jce.tar
285
KB
-rw-r--r--
jce.tar.gz
258.77
KB
-rw-r--r--
jce.zip
263.86
KB
-rw-r--r--
jcrop.zip
24.83
KB
-rw-r--r--
joomla.png.png.tar.gz
10.05
KB
-rw-r--r--
joomla.png.tar
12
KB
-rw-r--r--
joomla.tar
2.09
MB
-rw-r--r--
joomla.tar.gz
34.7
KB
-rw-r--r--
joomla.zip
1.86
MB
-rw-r--r--
joomla_black.png.png.tar.gz
5.04
KB
-rw-r--r--
joomla_black.png.tar
6.5
KB
-rw-r--r--
jquery-1.10.2.js.10.2.js.tar.g...
79.27
KB
-rw-r--r--
jquery-1.10.2.js.tar
268.5
KB
-rw-r--r--
jquery-1.7.2.js.7.2.js.tar.gz
32.95
KB
-rw-r--r--
jquery-1.7.2.js.tar
94.5
KB
-rw-r--r--
jquery-3.1.1.min.js.1.1.min.js...
29.5
KB
-rw-r--r--
jquery-3.1.1.min.js.tar
86.5
KB
-rw-r--r--
jquery-3.3.1.min.js.3.1.min.js...
23.63
KB
-rw-r--r--
jquery-3.3.1.min.js.tar
70
KB
-rw-r--r--
jquery.prettyPhoto.js.prettyPh...
6.23
KB
-rw-r--r--
jquery.prettyPhoto.js.tar
37.5
KB
-rw-r--r--
jquery.quicksand.js.quicksand....
3.32
KB
-rw-r--r--
jquery.quicksand.js.tar
16
KB
-rw-r--r--
jquery.tar
1.31
MB
-rw-r--r--
jquery.tar.gz
354.74
KB
-rw-r--r--
js.png.png.tar.gz
5.12
KB
-rw-r--r--
js.png.tar
7
KB
-rw-r--r--
js.tar
28.98
MB
-rw-r--r--
js.tar.gz
88.56
KB
-rw-r--r--
js.zip
28.81
MB
-rw-r--r--
jui.tar
1.93
MB
-rw-r--r--
jui.tar.gz
608.39
KB
-rw-r--r--
jui.zip
1.86
MB
-rw-r--r--
jw_allvideos.tar
8.5
KB
-rw-r--r--
jw_allvideos.tar.gz
6.89
KB
-rw-r--r--
jw_allvideos.zip
6.93
KB
-rw-r--r--
k2.tar
19.06
MB
-rw-r--r--
k2.tar.gz
12.15
MB
-rw-r--r--
k2.zip
20.62
MB
-rw-r--r--
keychain.php.php.tar.gz
2.5
KB
-rw-r--r--
keychain.php.tar
10
KB
-rw-r--r--
keys.tar
83
KB
-rw-r--r--
keys.tar.gz
24.92
KB
-rw-r--r--
keys.zip
80.85
KB
-rw-r--r--
kses.php.php.tar.gz
15.21
KB
-rw-r--r--
kses.php.tar
137
KB
-rw-r--r--
l10n.php.php.tar.gz
10.36
KB
-rw-r--r--
l10n.php.tar
123
KB
-rw-r--r--
l10n.tar
35.5
KB
-rw-r--r--
l10n.tar.gz
6.3
KB
-rw-r--r--
l10n.zip
31.37
KB
-rw-r--r--
langmetadata.xml.tar
4
KB
-rw-r--r--
langmetadata.xml.xml.tar.gz
602
B
-rw-r--r--
language.tar
4.46
MB
-rw-r--r--
language.tar.gz
712.33
KB
-rw-r--r--
language.zip
901.86
KB
-rw-r--r--
languages.tar
45.74
MB
-rw-r--r--
languages.tar.gz
10.09
MB
-rw-r--r--
languages.zip
45.4
MB
-rw-r--r--
latest-posts.tar
18
KB
-rw-r--r--
latest-posts.tar.gz
1.95
KB
-rw-r--r--
latest-posts.zip
12.74
KB
-rw-r--r--
layerslider.tar
742.5
KB
-rw-r--r--
layerslider.tar.gz
377.05
KB
-rw-r--r--
layerslider.zip
702.11
KB
-rw-r--r--
layouts.tar
576
KB
-rw-r--r--
layouts.tar.gz
64.14
KB
-rw-r--r--
layouts.zip
472.05
KB
-rw-r--r--
libraries.tar
65.01
MB
-rw-r--r--
libraries.tar.gz
3.69
MB
-rw-r--r--
libraries.zip
65.53
MB
-rw-r--r--
library.tar
2
KB
-rw-r--r--
library.tar.gz
263
B
-rw-r--r--
license.txt.tar
241
KB
-rw-r--r--
license.txt.txt.tar.gz
7.23
KB
-rw-r--r--
link-add.php.php.tar.gz
590
B
-rw-r--r--
link-add.php.tar
2.5
KB
-rw-r--r--
link-manager.php.php.tar.gz
1.88
KB
-rw-r--r--
link-manager.php.tar
6
KB
-rw-r--r--
link-parse-opml.php.php.tar.gz
1.09
KB
-rw-r--r--
link-parse-opml.php.tar
4.5
KB
-rw-r--r--
link-template.php.php.tar.gz
25.97
KB
-rw-r--r--
link-template.php.tar
154
KB
-rw-r--r--
link.php.php.tar.gz
1.15
KB
-rw-r--r--
link.php.tar
4.5
KB
-rw-r--r--
list.php.php.tar.gz
699
B
-rw-r--r--
list.php.tar
3
KB
-rw-r--r--
load-scripts.php.php.tar.gz
939
B
-rw-r--r--
load-scripts.php.tar
6.5
KB
-rw-r--r--
load-styles.php.php.tar.gz
1.14
KB
-rw-r--r--
load-styles.php.tar
8
KB
-rw-r--r--
load.php.php.tar.gz
14.85
KB
-rw-r--r--
load.php.tar
56
KB
-rw-r--r--
loader.gif.gif.tar.gz
2.23
KB
-rw-r--r--
loader.gif.tar
4
KB
-rw-r--r--
loader.php.php.tar.gz
5.63
KB
-rw-r--r--
loader.php.tar
24
KB
-rw-r--r--
locale.php.php.tar.gz
245
B
-rw-r--r--
locale.php.tar
4
KB
-rw-r--r--
location.pdf.pdf.tar.gz
475.03
KB
-rw-r--r--
location.pdf.tar
495
KB
-rw-r--r--
log.tar
82
KB
-rw-r--r--
log.tar.gz
24.81
KB
-rw-r--r--
log.zip
80.62
KB
-rw-r--r--
login.tar
101.44
MB
-rw-r--r--
login.tar.gz
45.21
MB
-rw-r--r--
login.zip
96.15
MB
-rw-r--r--
loginout.zip
1.15
KB
-rw-r--r--
logo ala maze.ai.ai.tar.gz
1.36
MB
-rw-r--r--
logo ala maze.ai.tar
1.42
MB
-rw-r--r--
logo-white.png.png.tar.gz
4.59
KB
-rw-r--r--
logo-white.png.tar
6
KB
-rw-r--r--
logo.png.png.tar.gz
92.76
KB
-rw-r--r--
logo.png.tar
211.5
KB
-rw-r--r--
logo.tar
497
KB
-rw-r--r--
logo.tar.gz
96
B
-rw-r--r--
logo.zip
493.46
KB
-rw-r--r--
logo2.png.png.tar.gz
3.13
KB
-rw-r--r--
logo2.png.tar
4.5
KB
-rw-r--r--
logo_2x.png.png.tar.gz
1.9
KB
-rw-r--r--
logo_2x.png.tar
3.5
KB
-rw-r--r--
logo_sticky.png.png.tar.gz
1.24
KB
-rw-r--r--
logo_sticky.png.tar
3
KB
-rw-r--r--
logoindex2.png.png.tar.gz
3.89
KB
-rw-r--r--
logoindex2.png.tar
10
KB
-rw-r--r--
logos.tar
1.5
KB
-rw-r--r--
logos.tar.gz
97
B
-rw-r--r--
logowhite.png.png.tar.gz
3.98
KB
-rw-r--r--
logowhite.png.tar
6
KB
-rw-r--r--
logrotation.zip
8.03
KB
-rw-r--r--
logs.tar
117.5
KB
-rw-r--r--
logs.tar.gz
25.54
KB
-rw-r--r--
logs.zip
353.88
KB
-rw-r--r--
magento.png.png.tar.gz
50.73
KB
-rw-r--r--
magento.png.tar
60.5
KB
-rw-r--r--
mailto.tar
2
KB
-rw-r--r--
mailto.tar.gz
208
B
-rw-r--r--
mailto.zip
284
B
-rw-r--r--
main-banner_old2.jpg.jpg.tar.g...
61.7
KB
-rw-r--r--
main-banner_old2.jpg.tar
95.5
KB
-rw-r--r--
main.css.css.tar.gz
2.27
KB
-rw-r--r--
main.css.tar
10.5
KB
-rw-r--r--
main.js.js.tar.gz
1.83
KB
-rw-r--r--
main.js.tar
7
KB
-rw-r--r--
main.min.css.min.css.tar.gz
1.97
KB
-rw-r--r--
main.min.css.tar
8
KB
-rw-r--r--
main.min.js.min.js.tar.gz
1.66
KB
-rw-r--r--
main.min.js.tar
6
KB
-rw-r--r--
maint.tar
9
KB
-rw-r--r--
maint.tar.gz
2.66
KB
-rw-r--r--
maint.zip
7.56
KB
-rw-r--r--
manifest.json.json.tar.gz
294
B
-rw-r--r--
manifest.json.tar
2.5
KB
-rw-r--r--
mapmarker_func.jquery.js.jquer...
317
B
-rw-r--r--
mapmarker_func.jquery.js.tar
2
KB
-rw-r--r--
maps.tar
79.5
KB
-rw-r--r--
maps.tar.gz
19.9
KB
-rw-r--r--
maps.zip
77.71
KB
-rw-r--r--
media-audiovideo.js.js.tar.gz
6.02
KB
-rw-r--r--
media-audiovideo.js.tar
28.5
KB
-rw-r--r--
media-button-2x.png.png.tar.gz
1007
B
-rw-r--r--
media-button-2x.png.tar
2.5
KB
-rw-r--r--
media-button-image.gif.gif.tar...
325
B
-rw-r--r--
media-button-image.gif.tar
2
KB
-rw-r--r--
media-button-other.gif.gif.tar...
409
B
-rw-r--r--
media-button-other.gif.tar
2
KB
-rw-r--r--
media-button.png.png.tar.gz
475
B
-rw-r--r--
media-button.png.tar
2
KB
-rw-r--r--
media-new.php.php.tar.gz
1.57
KB
-rw-r--r--
media-new.php.tar
9
KB
-rw-r--r--
media-template.php.php.tar.gz
11.15
KB
-rw-r--r--
media-template.php.tar
184.5
KB
-rw-r--r--
media-text.tar
22
KB
-rw-r--r--
media-text.tar.gz
2.2
KB
-rw-r--r--
media-text.zip
15.34
KB
-rw-r--r--
media-upload.php.php.tar.gz
1.46
KB
-rw-r--r--
media-upload.php.tar
13
KB
-rw-r--r--
media.php.php.tar.gz
36.43
KB
-rw-r--r--
media.php.tar
368.5
KB
-rw-r--r--
media.tar
76.18
MB
-rw-r--r--
media.tar.gz
16.27
MB
-rw-r--r--
media.zip
69.29
MB
-rw-r--r--
mediaelement.tar
2.19
MB
-rw-r--r--
mediaelement.tar.gz
154.85
KB
-rw-r--r--
mediaelement.zip
2.17
MB
-rw-r--r--
member.jpg.jpg.tar.gz
8.99
KB
-rw-r--r--
member.jpg.tar
11
KB
-rw-r--r--
menu-header.php.php.tar.gz
2.89
KB
-rw-r--r--
menu-header.php.tar
11.5
KB
-rw-r--r--
menu.php.php.tar.gz
4.47
KB
-rw-r--r--
menu.php.tar
18.5
KB
-rw-r--r--
menu.tar
5.5
KB
-rw-r--r--
menu.tar.gz
1.26
KB
-rw-r--r--
meta-boxes.php.php.tar.gz
12.82
KB
-rw-r--r--
meta-boxes.php.tar
185.5
KB
-rw-r--r--
meta.php.php.tar.gz
9.97
KB
-rw-r--r--
meta.php.tar
290
KB
-rw-r--r--
metismenujs.tar
77
KB
-rw-r--r--
metismenujs.tar.gz
16.01
KB
-rw-r--r--
middletv.tar
17.4
MB
-rw-r--r--
middletv.tar.gz
9.32
MB
-rw-r--r--
middletv.zip
17.23
MB
-rw-r--r--
missing.tar
2.5
KB
-rw-r--r--
missing.tar.gz
396
B
-rw-r--r--
mod_acymailing.zip
60.47
KB
-rw-r--r--
mod_articles_latest.tar
13
KB
-rw-r--r--
mod_articles_latest.tar.gz
2.95
KB
-rw-r--r--
mod_articles_latest.zip
9.55
KB
-rw-r--r--
mod_articles_news.tar
23.5
KB
-rw-r--r--
mod_articles_news.tar.gz
4.22
KB
-rw-r--r--
mod_articles_popular.zip
9.37
KB
-rw-r--r--
mod_breadcrumbs.tar
13
KB
-rw-r--r--
mod_breadcrumbs.tar.gz
2.95
KB
-rw-r--r--
mod_breadcrumbs.zip
9.32
KB
-rw-r--r--
mod_custom.tar
7
KB
-rw-r--r--
mod_custom.tar.gz
1.57
KB
-rw-r--r--
mod_custom.zip
4.06
KB
-rw-r--r--
mod_falang.tar
50.5
KB
-rw-r--r--
mod_falang.tar.gz
9.35
KB
-rw-r--r--
mod_falang.zip
44.2
KB
-rw-r--r--
mod_feed.zip
10.14
KB
-rw-r--r--
mod_finder.tar
18
KB
-rw-r--r--
mod_finder.tar.gz
4.23
KB
-rw-r--r--
mod_hikashop_filter.tar
15
KB
-rw-r--r--
mod_hikashop_filter.tar.gz
2.09
KB
-rw-r--r--
mod_k2_tools.tar
88
KB
-rw-r--r--
mod_k2_tools.tar.gz
13.73
KB
-rw-r--r--
mod_k2_tools.zip
79.47
KB
-rw-r--r--
mod_languages.tar
18.5
KB
-rw-r--r--
mod_languages.tar.gz
3.59
KB
-rw-r--r--
mod_languages.zip
14.83
KB
-rw-r--r--
mod_multilangstatus.tar
8
KB
-rw-r--r--
mod_multilangstatus.tar.gz
1.17
KB
-rw-r--r--
mod_privacy_dashboard.tar
10.5
KB
-rw-r--r--
mod_privacy_dashboard.tar.gz
2.18
KB
-rw-r--r--
mod_privacy_dashboard.zip
6.72
KB
-rw-r--r--
mod_related_items.zip
8.91
KB
-rw-r--r--
mod_search.tar
13
KB
-rw-r--r--
mod_search.tar.gz
2.86
KB
-rw-r--r--
mod_search.zip
9.7
KB
-rw-r--r--
mod_spsimpleportfolio.tar
25.5
KB
-rw-r--r--
mod_spsimpleportfolio.tar.gz
5.22
KB
-rw-r--r--
mod_sr_currency.tar
30.5
KB
-rw-r--r--
mod_sr_currency.tar.gz
3.72
KB
-rw-r--r--
mod_stats.tar
16.5
KB
-rw-r--r--
mod_stats.tar.gz
2.36
KB
-rw-r--r--
mod_stats.zip
12.43
KB
-rw-r--r--
mod_submenu.tar
7
KB
-rw-r--r--
mod_submenu.tar.gz
1.56
KB
-rw-r--r--
mod_syndicate.tar
8.5
KB
-rw-r--r--
mod_syndicate.tar.gz
1.77
KB
-rw-r--r--
mod_syndicate.zip
5.02
KB
-rw-r--r--
mod_tags_popular.zip
13.4
KB
-rw-r--r--
mod_tags_similar.tar
14
KB
-rw-r--r--
mod_tags_similar.tar.gz
3.23
KB
-rw-r--r--
mod_tags_similar.zip
11.02
KB
-rw-r--r--
mod_users_latest.tar
9
KB
-rw-r--r--
mod_users_latest.tar.gz
2.01
KB
-rw-r--r--
mod_users_latest.zip
5.81
KB
-rw-r--r--
mod_wrapper.tar
11
KB
-rw-r--r--
mod_wrapper.tar.gz
2.21
KB
-rw-r--r--
mod_wrapper.zip
7.83
KB
-rw-r--r--
modal.zip
17.12
KB
-rw-r--r--
models.tar
6.5
KB
-rw-r--r--
models.tar.gz
1.57
KB
-rw-r--r--
moderation.php.php.tar.gz
303
B
-rw-r--r--
moderation.php.tar
2
KB
-rw-r--r--
modules.tar
2.56
MB
-rw-r--r--
modules.tar.gz
87.17
KB
-rw-r--r--
modules.zip
3.04
MB
-rw-r--r--
more.zip
4.38
KB
-rw-r--r--
ms-admin.php.php.tar.gz
263
B
-rw-r--r--
ms-admin.php.tar
2
KB
-rw-r--r--
ms-blogs.php.php.tar.gz
6.12
KB
-rw-r--r--
ms-blogs.php.tar
27
KB
-rw-r--r--
ms-default-constants.php.php.t...
1.66
KB
-rw-r--r--
ms-default-constants.php.tar
23
KB
-rw-r--r--
ms-default-filters.php.php.tar...
1.8
KB
-rw-r--r--
ms-default-filters.php.tar
27.5
KB
-rw-r--r--
ms-delete-site.php.php.tar.gz
1.87
KB
-rw-r--r--
ms-delete-site.php.tar
11
KB
-rw-r--r--
ms-deprecated.php.php.tar.gz
6.25
KB
-rw-r--r--
ms-deprecated.php.tar
23
KB
-rw-r--r--
ms-edit.php.php.tar.gz
280
B
-rw-r--r--
ms-edit.php.tar
3
KB
-rw-r--r--
ms-files.php.php.tar.gz
1.22
KB
-rw-r--r--
ms-files.php.tar
8
KB
-rw-r--r--
ms-functions.php.php.tar.gz
19.22
KB
-rw-r--r--
ms-functions.php.tar
91
KB
-rw-r--r--
ms-icon-144x144.png.png.tar.gz
14.3
KB
-rw-r--r--
ms-icon-144x144.png.tar
16
KB
-rw-r--r--
ms-icon-150x150.png.png.tar.gz
14.94
KB
-rw-r--r--
ms-icon-150x150.png.tar
16.5
KB
-rw-r--r--
ms-icon-310x310.png.png.tar.gz
43.67
KB
-rw-r--r--
ms-icon-310x310.png.tar
45.5
KB
-rw-r--r--
ms-icon-70x70.png.png.tar.gz
5.71
KB
-rw-r--r--
ms-icon-70x70.png.tar
7.5
KB
-rw-r--r--
ms-load.php.php.tar.gz
6.03
KB
-rw-r--r--
ms-load.php.tar
40.5
KB
-rw-r--r--
ms-network.php.php.tar.gz
1.47
KB
-rw-r--r--
ms-network.php.tar
10
KB
-rw-r--r--
ms-options.php.php.tar.gz
282
B
-rw-r--r--
ms-options.php.tar
2
KB
-rw-r--r--
ms-settings.php.php.tar.gz
1.62
KB
-rw-r--r--
ms-settings.php.tar
11
KB
-rw-r--r--
ms-site.php.php.tar.gz
9.44
KB
-rw-r--r--
ms-site.php.tar
41
KB
-rw-r--r--
ms-sites.php.php.tar.gz
273
B
-rw-r--r--
ms-sites.php.tar
3
KB
-rw-r--r--
ms-upgrade-network.php.php.tar...
281
B
-rw-r--r--
ms-upgrade-network.php.tar
2
KB
-rw-r--r--
ms-users.php.php.tar.gz
274
B
-rw-r--r--
ms-users.php.tar
3
KB
-rw-r--r--
mu-plugins.tar
2.5
KB
-rw-r--r--
mu-plugins.tar.gz
393
B
-rw-r--r--
mu-plugins.zip
791
B
-rw-r--r--
my-music.online.tar
636.9
MB
-rw-r--r--
my-music.online.tar.gz
20
B
-rw-r--r--
my-sites.php.php.tar.gz
2.04
KB
-rw-r--r--
my-sites.php.tar
6.5
KB
-rw-r--r--
mymusicpromo-online.zip
53.39
MB
-rw-r--r--
mysql.png.png.tar.gz
4.7
KB
-rw-r--r--
mysql.png.tar
7
KB
-rw-r--r--
nav-menu-template.php.php.tar....
6.24
KB
-rw-r--r--
nav-menu-template.php.tar
76.5
KB
-rw-r--r--
nav-menu.php.php.tar.gz
9.58
KB
-rw-r--r--
nav-menu.php.tar
45
KB
-rw-r--r--
nav-menus.php.php.tar.gz
10.56
KB
-rw-r--r--
nav-menus.php.tar
50
KB
-rw-r--r--
navigation-link.tar
19.5
KB
-rw-r--r--
navigation-link.tar.gz
1.97
KB
-rw-r--r--
navigation-submenu.zip
9.26
KB
-rw-r--r--
navigation.zip
131.13
KB
-rw-r--r--
network.php.php.tar.gz
2.23
KB
-rw-r--r--
network.php.tar
7
KB
-rw-r--r--
network.tar
145.5
KB
-rw-r--r--
network.tar.gz
27.6
KB
-rw-r--r--
network.zip
127.06
KB
-rw-r--r--
networks.php.php.tar.gz
1.07
KB
-rw-r--r--
networks.php.tar
3.5
KB
-rw-r--r--
newsletter.php.php.tar.gz
1.39
KB
-rw-r--r--
newsletter.php.tar
4
KB
-rw-r--r--
nl-NL.tar
354.5
KB
-rw-r--r--
nl-NL.tar.gz
79.1
KB
-rw-r--r--
nl-NL.zip
304
KB
-rw-r--r--
node.png.png.tar.gz
51.8
KB
-rw-r--r--
node.png.tar
57.5
KB
-rw-r--r--
node2.png.png.tar.gz
26.86
KB
-rw-r--r--
node2.png.tar
32
KB
-rw-r--r--
odfiafrica.jpg.jpg.tar.gz
384.95
KB
-rw-r--r--
odfiafrica.jpg.tar
417
KB
-rw-r--r--
onepage-slider.html.html.tar.g...
6.99
KB
-rw-r--r--
onepage-slider.html.tar
42.5
KB
-rw-r--r--
onepage-text.html.html.tar.gz
6.9
KB
-rw-r--r--
onepage-text.html.tar
42
KB
-rw-r--r--
option.php.php.tar.gz
13.11
KB
-rw-r--r--
option.php.tar
532.5
KB
-rw-r--r--
options-discussion.php.php.tar...
4.27
KB
-rw-r--r--
options-discussion.php.tar
82
KB
-rw-r--r--
options-general.php.php.tar.gz
6.41
KB
-rw-r--r--
options-general.php.tar
83
KB
-rw-r--r--
options-head.php.php.tar.gz
464
B
-rw-r--r--
options-head.php.tar
2.5
KB
-rw-r--r--
options-permalink.php.php.tar....
5.59
KB
-rw-r--r--
options-permalink.php.tar
88.5
KB
-rw-r--r--
options-privacy.php.php.tar.gz
2.71
KB
-rw-r--r--
options-privacy.php.tar
41
KB
-rw-r--r--
options-reading.php.php.tar.gz
3.1
KB
-rw-r--r--
options-reading.php.tar
12
KB
-rw-r--r--
options-writing.php.php.tar.gz
3.05
KB
-rw-r--r--
options-writing.php.tar
21
KB
-rw-r--r--
options.php.php.tar.gz
4.51
KB
-rw-r--r--
options.php.tar
56.5
KB
-rw-r--r--
overrider.tar
12.5
KB
-rw-r--r--
overrider.tar.gz
2.86
KB
-rw-r--r--
overrider.zip
9.83
KB
-rw-r--r--
overrides.tar
3
KB
-rw-r--r--
overrides.tar.gz
269
B
-rw-r--r--
overrides.zip
512
B
-rw-r--r--
owl.carousel.js.carousel.js.ta...
17.24
KB
-rw-r--r--
owl.carousel.js.tar
79
KB
-rw-r--r--
owl.carousel.min.js.carousel.m...
10.39
KB
-rw-r--r--
owl.carousel.min.js.tar
89
KB
-rw-r--r--
p1.jpg.jpg.tar.gz
237.02
KB
-rw-r--r--
p1.jpg.tar
243.5
KB
-rw-r--r--
p2.jpg.jpg.tar.gz
283.15
KB
-rw-r--r--
p2.jpg.tar
285
KB
-rw-r--r--
p3.jpg.jpg.tar.gz
287.59
KB
-rw-r--r--
p3.jpg.tar
289.5
KB
-rw-r--r--
p4.jpg.jpg.tar.gz
394.73
KB
-rw-r--r--
p4.jpg.tar
396.5
KB
-rw-r--r--
p5.jpg.jpg.tar.gz
177.65
KB
-rw-r--r--
p5.jpg.tar
181
KB
-rw-r--r--
p6.jpg.jpg.tar.gz
601.57
KB
-rw-r--r--
p6.jpg.tar
603
KB
-rw-r--r--
packageinstaller.zip
11.38
KB
-rw-r--r--
page-list-item.tar
3
KB
-rw-r--r--
page-list-item.tar.gz
576
B
-rw-r--r--
page-list.tar
15
KB
-rw-r--r--
page-list.tar.gz
1.39
KB
-rw-r--r--
pagebuilder.tar
6.09
MB
-rw-r--r--
pagebuilder.tar.gz
5.84
MB
-rw-r--r--
paralax-index1_old.jpg.jpg.tar...
794.32
KB
-rw-r--r--
paralax-index1_old.jpg.tar
807.5
KB
-rw-r--r--
particles.min.js.min.js.tar.gz
5.91
KB
-rw-r--r--
particles.min.js.tar
24.5
KB
-rw-r--r--
pattern.png.png.tar.gz
2.75
KB
-rw-r--r--
pattern.png.tar
4.5
KB
-rw-r--r--
pattern.svg.svg.tar.gz
17.24
KB
-rw-r--r--
pattern.svg.tar
53
KB
-rw-r--r--
pattern.zip
478
B
-rw-r--r--
payeer_1314567119.txt.tar
2
KB
-rw-r--r--
payeer_1314567119.txt.txt.tar....
128
B
-rw-r--r--
payments.png.png.tar.gz
10.04
KB
-rw-r--r--
payments.png.tar
11.5
KB
-rw-r--r--
payments_2x.png.png.tar.gz
31.26
KB
-rw-r--r--
payments_2x.png.tar
33
KB
-rw-r--r--
paypal_bt.png.png.tar.gz
13.5
KB
-rw-r--r--
paypal_bt.png.tar
15
KB
-rw-r--r--
paypaladvanced_2_6279148dd013c...
334
B
-rw-r--r--
paypaladvanced_2_6279148dd013c...
2.5
KB
-rw-r--r--
paypaladvanced_cancel_2.php.ph...
316
B
-rw-r--r--
paypaladvanced_cancel_2.php.ta...
2.5
KB
-rw-r--r--
paypaladvanced_return_2.php.ph...
316
B
-rw-r--r--
paypaladvanced_return_2.php.ta...
2.5
KB
-rw-r--r--
php-compat.tar
3
KB
-rw-r--r--
php-compat.tar.gz
649
B
-rw-r--r--
php-compat.zip
1.38
KB
-rw-r--r--
php-encryption.tar
26
KB
-rw-r--r--
php-encryption.tar.gz
6.52
KB
-rw-r--r--
php-encryption.zip
24.3
KB
-rw-r--r--
php.ini.ini.tar.gz
244
B
-rw-r--r--
php.ini.tar
3
KB
-rw-r--r--
php.png.png.tar.gz
4.91
KB
-rw-r--r--
php.png.tar
6.5
KB
-rw-r--r--
php_mail.log.log.tar.gz
6.45
KB
-rw-r--r--
php_mail.log.tar
100.5
KB
-rw-r--r--
pickapp.tar
83.5
KB
-rw-r--r--
pickapp.tar.gz
25.16
KB
-rw-r--r--
plg_installer_webinstaller.tar
34.5
KB
-rw-r--r--
plg_installer_webinstaller.tar...
6.53
KB
-rw-r--r--
plg_installer_webinstaller.zip
31.15
KB
-rw-r--r--
plg_media-action_crop.tar
19
KB
-rw-r--r--
plg_media-action_crop.tar.gz
4.14
KB
-rw-r--r--
plg_media-action_crop.zip
14.32
KB
-rw-r--r--
plg_media-action_resize.tar
15
KB
-rw-r--r--
plg_media-action_resize.tar.gz
3.31
KB
-rw-r--r--
plg_media-action_resize.zip
10.3
KB
-rw-r--r--
plg_media-action_rotate.tar
17
KB
-rw-r--r--
plg_media-action_rotate.tar.gz
3.98
KB
-rw-r--r--
plg_media-action_rotate.zip
12.17
KB
-rw-r--r--
plg_quickicon_extensionupdate....
3.5
KB
-rw-r--r--
plg_quickicon_extensionupdate....
791
B
-rw-r--r--
plg_quickicon_extensionupdate....
7.69
KB
-rw-r--r--
plg_quickicon_joomlaupdate.tar
4
KB
-rw-r--r--
plg_quickicon_joomlaupdate.tar...
890
B
-rw-r--r--
plg_quickicon_privacycheck.tar
3.5
KB
-rw-r--r--
plg_quickicon_privacycheck.tar...
860
B
-rw-r--r--
plg_quickicon_privacycheck.zip
2.12
KB
-rw-r--r--
plg_solidres_limitbooking.tar
6
KB
-rw-r--r--
plg_solidres_limitbooking.tar....
1.18
KB
-rw-r--r--
plg_system_highlight.tar
2
KB
-rw-r--r--
plg_system_highlight.tar.gz
166
B
-rw-r--r--
plg_system_highlight.zip
241
B
-rw-r--r--
plg_system_stats.tar
4
KB
-rw-r--r--
plg_system_stats.tar.gz
841
B
-rw-r--r--
plg_system_stats.zip
2.31
KB
-rw-r--r--
plg_twofactorauth_totp.tar
117
KB
-rw-r--r--
plg_twofactorauth_totp.tar.gz
49.11
KB
-rw-r--r--
plg_twofactorauth_totp.zip
113.39
KB
-rw-r--r--
pluggable-deprecated.php.php.t...
1.97
KB
-rw-r--r--
pluggable-deprecated.php.tar
8
KB
-rw-r--r--
pluggable.php.php.tar.gz
23.38
KB
-rw-r--r--
pluggable.php.tar
213
KB
-rw-r--r--
plugin-editor.php.php.tar.gz
4.7
KB
-rw-r--r--
plugin-editor.php.tar
15
KB
-rw-r--r--
plugin-install.php.php.tar.gz
2.64
KB
-rw-r--r--
plugin-install.php.tar
30
KB
-rw-r--r--
plugin.php.php.tar.gz
7.14
KB
-rw-r--r--
plugin.php.tar
36.5
KB
-rw-r--r--
plugin.tar
366
KB
-rw-r--r--
plugin.tar.gz
90.03
KB
-rw-r--r--
plugins.php.php.tar.gz
1.08
KB
-rw-r--r--
plugins.php.tar
86
KB
-rw-r--r--
plugins.tar
239.95
MB
-rw-r--r--
plugins.tar.gz
1.59
MB
-rw-r--r--
plugins.zip
715.26
MB
-rw-r--r--
plugins1.tar
9.22
MB
-rw-r--r--
plugins1.tar.gz
4.62
MB
-rw-r--r--
plugins1.zip
9.2
MB
-rw-r--r--
plupload.tar
486.5
KB
-rw-r--r--
plupload.tar.gz
134.75
KB
-rw-r--r--
pomo.tar
59
KB
-rw-r--r--
pomo.tar.gz
11.77
KB
-rw-r--r--
pomo.zip
54
KB
-rw-r--r--
popper.min.js.min.js.tar.gz
7.4
KB
-rw-r--r--
popper.min.js.tar
22.5
KB
-rw-r--r--
popup_img.jpg.jpg.tar.gz
17.31
KB
-rw-r--r--
popup_img.jpg.tar
19
KB
-rw-r--r--
portfolio.html.html.tar.gz
3.03
KB
-rw-r--r--
portfolio.html.tar
17.5
KB
-rw-r--r--
post-comments-form.zip
10.75
KB
-rw-r--r--
post-content.zip
2.05
KB
-rw-r--r--
post-excerpt.php.php.tar.gz
1.45
KB
-rw-r--r--
post-excerpt.php.tar
5
KB
-rw-r--r--
post-excerpt.tar
11
KB
-rw-r--r--
post-excerpt.tar.gz
919
B
-rw-r--r--
post-formats.php.php.tar.gz
1.97
KB
-rw-r--r--
post-formats.php.tar
16
KB
-rw-r--r--
post-new.php.php.tar.gz
1.13
KB
-rw-r--r--
post-new.php.tar
15
KB
-rw-r--r--
post-template.php.php.tar.gz
15.54
KB
-rw-r--r--
post-template.php.tar
67
KB
-rw-r--r--
post-template.tar
16
KB
-rw-r--r--
post-template.tar.gz
1.44
KB
-rw-r--r--
post-template.zip
9.12
KB
-rw-r--r--
post-thumbnail-template.php.ph...
2.5
KB
-rw-r--r--
post-thumbnail-template.php.ta...
33.5
KB
-rw-r--r--
post.php.php.tar.gz
3.01
KB
-rw-r--r--
post.php.tar
1.12
MB
-rw-r--r--
postnews.php.php.tar.gz
2.34
KB
-rw-r--r--
postnews.php.tar
4
KB
-rw-r--r--
posts.tar
1.41
MB
-rw-r--r--
posts.tar.gz
1.36
MB
-rw-r--r--
posts.zip
1.4
MB
-rw-r--r--
powered_by.png.png.tar.gz
2.3
KB
-rw-r--r--
powered_by.png.tar
8
KB
-rw-r--r--
premium-addons-elementor.tar
69.5
KB
-rw-r--r--
premium-addons-elementor.tar.g...
8.32
KB
-rw-r--r--
premium-addons-elementor.zip
90.58
KB
-rw-r--r--
press-this.php.php.tar.gz
1.07
KB
-rw-r--r--
press-this.php.tar
7
KB
-rw-r--r--
prestashop.png.png.tar.gz
9.5
KB
-rw-r--r--
prestashop.png.tar
11
KB
-rw-r--r--
prev-blue.png.png.tar.gz
3.8
KB
-rw-r--r--
prev-blue.png.tar
5.5
KB
-rw-r--r--
prev-dark.png.png.tar.gz
3.78
KB
-rw-r--r--
prev-dark.png.tar
5.5
KB
-rw-r--r--
prev-green.png.png.tar.gz
3.88
KB
-rw-r--r--
prev-green.png.tar
5.5
KB
-rw-r--r--
prev-orange.png.png.tar.gz
3.87
KB
-rw-r--r--
prev-orange.png.tar
5.5
KB
-rw-r--r--
prev.png.png.tar.gz
3.88
KB
-rw-r--r--
prev.png.tar
10
KB
-rw-r--r--
preview.html.html.tar.gz
1.56
KB
-rw-r--r--
preview.html.tar
8.5
KB
-rw-r--r--
pricing.html.html.tar.gz
3.05
KB
-rw-r--r--
pricing.html.tar
14.5
KB
-rw-r--r--
privacy-policy-guide.php.php.t...
1.52
KB
-rw-r--r--
privacy-policy-guide.php.tar
19
KB
-rw-r--r--
privacy.php.php.tar.gz
1.12
KB
-rw-r--r--
privacy.php.tar
14.5
KB
-rw-r--r--
privacy.tar
62
KB
-rw-r--r--
privacy.tar.gz
7.92
KB
-rw-r--r--
privacy.zip
49.12
KB
-rw-r--r--
privacyconsent.tar
32
KB
-rw-r--r--
privacyconsent.tar.gz
7.52
KB
-rw-r--r--
privacyconsent.zip
28.66
KB
-rw-r--r--
product_edit_dimensions.php.ph...
1.11
KB
-rw-r--r--
product_edit_dimensions.php.ta...
5
KB
-rw-r--r--
profile.php.php.tar.gz
314
B
-rw-r--r--
profile.php.tar
4
KB
-rw-r--r--
promo_bg.jpg.jpg.tar.gz
7.8
KB
-rw-r--r--
promo_bg.jpg.tar
19
KB
-rw-r--r--
promotions.tar
16
KB
-rw-r--r--
promotions.tar.gz
2.24
KB
-rw-r--r--
promotions.zip
10.77
KB
-rw-r--r--
protostar.tar
430.5
KB
-rw-r--r--
protostar.tar.gz
213.28
KB
-rw-r--r--
protostar.zip
408.12
KB
-rw-r--r--
providers.zip
17.7
KB
-rw-r--r--
ptitsanes.jpg.jpg.tar.gz
207.01
KB
-rw-r--r--
ptitsanes.jpg.tar
234.5
KB
-rw-r--r--
ptitsanes.png.png.tar.gz
1.09
MB
-rw-r--r--
ptitsanes.png.tar
1.11
MB
-rw-r--r--
pullquote.tar
19.5
KB
-rw-r--r--
pullquote.tar.gz
1.48
KB
-rw-r--r--
pullquote.zip
18.03
KB
-rw-r--r--
pwnkit
0
B
-rwxr-xr-x
python.png.png.tar.gz
9.74
KB
-rw-r--r--
python.png.tar
12
KB
-rw-r--r--
q1.php.php.tar.gz
740
B
-rw-r--r--
q1.php.tar
8
KB
-rw-r--r--
qlf.txt.tar
2
KB
-rw-r--r--
qlf.txt.txt.tar.gz
120
B
-rw-r--r--
quaadra.tar
3.29
MB
-rw-r--r--
quaadra.tar.gz
2.6
MB
-rw-r--r--
query-no-results.tar
2.5
KB
-rw-r--r--
query-no-results.tar.gz
500
B
-rw-r--r--
query-pagination-next.zip
1.17
KB
-rw-r--r--
query.php.php.tar.gz
4.82
KB
-rw-r--r--
query.php.tar
183.5
KB
-rw-r--r--
query.zip
14.91
KB
-rw-r--r--
quickicon.tar
87
KB
-rw-r--r--
quickicon.tar.gz
10.07
KB
-rw-r--r--
quickicon.zip
69.9
KB
-rw-r--r--
quote.zip
7.6
KB
-rw-r--r--
readme.html.html.tar.gz
3.03
KB
-rw-r--r--
readme.html.tar
28
KB
-rw-r--r--
readme.txt.tar
6.5
KB
-rw-r--r--
readme.txt.txt.tar.gz
2.08
KB
-rw-r--r--
realex_hpp_api.tar
271.5
KB
-rw-r--r--
realex_hpp_api.tar.gz
53.14
KB
-rw-r--r--
registration-functions.php.php...
273
B
-rw-r--r--
registration-functions.php.tar
3
KB
-rw-r--r--
registration.php.php.tar.gz
266
B
-rw-r--r--
registration.php.tar
3
KB
-rw-r--r--
remember.tar
7.5
KB
-rw-r--r--
remember.tar.gz
1.93
KB
-rw-r--r--
remember.zip
4.68
KB
-rw-r--r--
rest-api.php.php.tar.gz
20.51
KB
-rw-r--r--
rest-api.php.tar
98
KB
-rw-r--r--
rest-api.tar
997
KB
-rw-r--r--
rest-api.tar.gz
168.1
KB
-rw-r--r--
rest-api.zip
965.07
KB
-rw-r--r--
retina-replace.js.js.tar.gz
1.39
KB
-rw-r--r--
retina-replace.js.tar
5
KB
-rw-r--r--
retina-replace.min.js.min.js.t...
1.08
KB
-rw-r--r--
retina-replace.min.js.tar
4
KB
-rw-r--r--
revision.php.php.tar.gz
2.11
KB
-rw-r--r--
revision.php.tar
38.5
KB
-rw-r--r--
rewrite.php.php.tar.gz
5.84
KB
-rw-r--r--
rewrite.php.tar
21
KB
-rw-r--r--
ribbon.png.png.tar.gz
12.26
KB
-rw-r--r--
ribbon.png.tar
14
KB
-rw-r--r--
rick.jpg.jpg.tar.gz
105.81
KB
-rw-r--r--
rick.jpg.tar
112.5
KB
-rw-r--r--
robots-template.php.php.tar.gz
1.31
KB
-rw-r--r--
robots-template.php.tar
13
KB
-rw-r--r--
robots.txt.dist.tar
8.5
KB
-rw-r--r--
robots.txt.dist.txt.dist.tar.g...
492
B
-rw-r--r--
robots.txt.tar
10
KB
-rw-r--r--
robots.txt.txt.tar.gz
213
B
-rw-r--r--
router.php.php.tar.gz
3.1
KB
-rw-r--r--
router.php.tar
16.5
KB
-rw-r--r--
royalkarukera.jpg.jpg.tar.gz
295.82
KB
-rw-r--r--
royalkarukera.jpg.tar
330.5
KB
-rw-r--r--
royalkarukera.png.png.tar.gz
1.45
MB
-rw-r--r--
royalkarukera.png.tar
1.48
MB
-rw-r--r--
royalkarukera.tar
165.02
MB
-rw-r--r--
royalkarukera.tar.gz
70.64
MB
-rw-r--r--
royalkarukerda.jpg.jpg.tar.gz
292.85
KB
-rw-r--r--
royalkarukerda.jpg.tar
326
KB
-rw-r--r--
rss-functions.php.php.tar.gz
312
B
-rw-r--r--
rss-functions.php.tar
2
KB
-rw-r--r--
rss.php.php.tar.gz
6.66
KB
-rw-r--r--
rss.php.tar
48
KB
-rw-r--r--
s1.PNG.PNG.tar.gz
1003.13
KB
-rw-r--r--
s1.PNG.tar
1005
KB
-rw-r--r--
s2.PNG.PNG.tar.gz
409.52
KB
-rw-r--r--
s2.PNG.tar
415
KB
-rw-r--r--
s3.PNG.PNG.tar.gz
753.06
KB
-rw-r--r--
s3.PNG.tar
757
KB
-rw-r--r--
s4.PNG.PNG.tar.gz
674.01
KB
-rw-r--r--
s4.PNG.tar
682.5
KB
-rw-r--r--
sampledata.tar
577
KB
-rw-r--r--
sampledata.tar.gz
5.79
KB
-rw-r--r--
sampledata.zip
1.18
MB
-rw-r--r--
script-loader.php.php.tar.gz
30.55
KB
-rw-r--r--
script-loader.php.tar
529
KB
-rw-r--r--
script-modules.php.php.tar.gz
1.16
KB
-rw-r--r--
script-modules.php.tar
7.5
KB
-rw-r--r--
script.js.js.tar.gz
739
B
-rw-r--r--
script.js.tar
3
KB
-rw-r--r--
scss.tar
143.5
KB
-rw-r--r--
scss.tar.gz
33.94
KB
-rw-r--r--
scss.zip
128.81
KB
-rw-r--r--
search.php.php.tar.gz
870
B
-rw-r--r--
search.php.tar
3
KB
-rw-r--r--
search.tar
57
KB
-rw-r--r--
search.tar.gz
7.01
KB
-rw-r--r--
search.zip
83.69
KB
-rw-r--r--
searchtools.zip
10.7
KB
-rw-r--r--
semantic.css.css.tar.gz
112.76
KB
-rw-r--r--
semantic.css.tar
735
KB
-rw-r--r--
semantic.js.js.tar.gz
113.67
KB
-rw-r--r--
semantic.js.tar
713
KB
-rw-r--r--
semantic.min.js.min.js.tar.gz
67.58
KB
-rw-r--r--
semantic.min.js.tar
270.5
KB
-rw-r--r--
sendmail.php.php.tar.gz
568
B
-rw-r--r--
sendmail.php.tar
2.5
KB
-rw-r--r--
service.html.html.tar.gz
3.74
KB
-rw-r--r--
service.html.tar
20.5
KB
-rw-r--r--
services.html.html.tar.gz
3.59
KB
-rw-r--r--
services.html.tar
16.5
KB
-rw-r--r--
session.php.php.tar.gz
277
B
-rw-r--r--
session.php.tar
3
KB
-rw-r--r--
sessionGc.php.php.tar.gz
768
B
-rw-r--r--
sessionGc.php.tar
7
KB
-rw-r--r--
sessionMetadataGc.php.php.tar....
866
B
-rw-r--r--
sessionMetadataGc.php.tar
3
KB
-rw-r--r--
setup-config.php.php.tar.gz
5.72
KB
-rw-r--r--
setup-config.php.tar
19
KB
-rw-r--r--
shape.png.png.tar.gz
1.48
KB
-rw-r--r--
shape.png.tar
3.5
KB
-rw-r--r--
shopify.png.png.tar.gz
231.5
KB
-rw-r--r--
shopify.png.tar
313.5
KB
-rw-r--r--
shortcode.tar
8
KB
-rw-r--r--
shortcode.tar.gz
803
B
-rw-r--r--
shortcodes.php.php.tar.gz
5.99
KB
-rw-r--r--
shortcodes.php.tar
46.5
KB
-rw-r--r--
showdemo.html.html.tar.gz
701
B
-rw-r--r--
showdemo.html.tar
3
KB
-rw-r--r--
single-post.html.html.tar.gz
3.59
KB
-rw-r--r--
single-post.html.tar
17.5
KB
-rw-r--r--
site-health-info.php.php.tar.g...
1.66
KB
-rw-r--r--
site-health-info.php.tar
5.5
KB
-rw-r--r--
site-health.php.php.tar.gz
3.56
KB
-rw-r--r--
site-health.php.tar
23
KB
-rw-r--r--
site-tagline.tar
7
KB
-rw-r--r--
site-tagline.tar.gz
815
B
-rw-r--r--
site-title.zip
3.47
KB
-rw-r--r--
sitemap.xml.tar
4.5
KB
-rw-r--r--
sitemap.xml.xml.tar.gz
588
B
-rw-r--r--
sitemaps.php.php.tar.gz
1.15
KB
-rw-r--r--
sitemaps.php.tar
5
KB
-rw-r--r--
sitemaps.tar
51
KB
-rw-r--r--
sitemaps.tar.gz
9.32
KB
-rw-r--r--
sitemaps.zip
45.39
KB
-rw-r--r--
skycons.js.js.tar.gz
4.57
KB
-rw-r--r--
skycons.js.tar
20.5
KB
-rw-r--r--
slides.tar
44.5
KB
-rw-r--r--
slides.tar.gz
8.34
KB
-rw-r--r--
slides.zip
41.88
KB
-rw-r--r--
snap.svg-min.js.svg-min.js.tar...
25.04
KB
-rw-r--r--
snap.svg-min.js.tar
73
KB
-rw-r--r--
social-links.zip
58.67
KB
-rw-r--r--
sodium_compat.tar
1.34
MB
-rw-r--r--
sodium_compat.tar.gz
217.7
KB
-rw-r--r--
sodium_compat.zip
1.28
MB
-rw-r--r--
solidres.zip
17.18
KB
-rw-r--r--
sound.tar
62.5
KB
-rw-r--r--
sound.tar.gz
59.95
KB
-rw-r--r--
sound.zip
60.97
KB
-rw-r--r--
spl-autoload-compat.php.php.ta...
412
B
-rw-r--r--
spl-autoload-compat.php.tar
4
KB
-rw-r--r--
spreadsheet.png.png.tar.gz
2.56
KB
-rw-r--r--
spreadsheet.png.tar
4
KB
-rw-r--r--
sprite-skin-flat.png.png.tar.g...
622
B
-rw-r--r--
sprite-skin-flat.png.tar
2.5
KB
-rw-r--r--
sprite.png.png.tar.gz
2.54
KB
-rw-r--r--
sprite.png.tar
4.5
KB
-rw-r--r--
src.tar
3.39
MB
-rw-r--r--
src.tar.gz
657.16
KB
-rw-r--r--
src.zip
3.12
MB
-rw-r--r--
ssl.tar
247.5
KB
-rw-r--r--
ssl.tar.gz
73.73
KB
-rw-r--r--
ssl.zip
243.2
KB
-rw-r--r--
stars.png.png.tar.gz
1.07
KB
-rw-r--r--
stars.png.tar
5
KB
-rw-r--r--
starter-license-W3Layouts.txt....
4
KB
-rw-r--r--
starter-license-W3Layouts.txt....
463
B
-rw-r--r--
stats.tar
31
KB
-rw-r--r--
stats.tar.gz
5.84
KB
-rw-r--r--
stats.zip
25.17
KB
-rw-r--r--
status.php.php.tar.gz
214
B
-rw-r--r--
status.php.tar
2
KB
-rw-r--r--
stories.tar
301
KB
-rw-r--r--
stories.tar.gz
241.65
KB
-rw-r--r--
stories.zip
8.1
MB
-rw-r--r--
stream-xchacha20.php.php.tar.g...
443
B
-rw-r--r--
stream-xchacha20.php.tar
3.5
KB
-rw-r--r--
streams.php.php.tar.gz
1.88
KB
-rw-r--r--
streams.php.tar
18
KB
-rw-r--r--
style-engine.php.php.tar.gz
2.08
KB
-rw-r--r--
style-engine.php.tar
25
KB
-rw-r--r--
style-engine.tar
52.5
KB
-rw-r--r--
style-engine.tar.gz
8.32
KB
-rw-r--r--
style-engine.zip
48.17
KB
-rw-r--r--
style-starter.css.css.tar.gz
44.05
KB
-rw-r--r--
style-starter.css.tar
307.5
KB
-rw-r--r--
style.css.css.tar.gz
5.02
KB
-rw-r--r--
style.css.tar
27
KB
-rw-r--r--
success.php.php.tar.gz
216
B
-rw-r--r--
success.php.tar
2
KB
-rw-r--r--
sulycast-theme.jpg.jpg.tar.gz
318.57
KB
-rw-r--r--
sulycast-theme.jpg.tar
350.5
KB
-rw-r--r--
svg.tar
16.5
KB
-rw-r--r--
svg.tar.gz
1.55
KB
-rw-r--r--
svg.zip
8.96
KB
-rw-r--r--
swfupload.zip
9.07
KB
-rw-r--r--
system.tar
14.56
MB
-rw-r--r--
system.tar.gz
3.55
MB
-rw-r--r--
system.zip
13.86
MB
-rw-r--r--
tab1.jpg.jpg.tar.gz
32.33
KB
-rw-r--r--
tab1.jpg.tar
34
KB
-rw-r--r--
tab2.jpg.jpg.tar.gz
41.76
KB
-rw-r--r--
tab2.jpg.tar
43.5
KB
-rw-r--r--
tab3.jpg.jpg.tar.gz
21.48
KB
-rw-r--r--
tab3.jpg.tar
23
KB
-rw-r--r--
taegesiy.php.php.tar.gz
529
B
-rw-r--r--
taegesiy.php.tar
2.5
KB
-rw-r--r--
tag-cloud.tar
9
KB
-rw-r--r--
tag-cloud.tar.gz
1.01
KB
-rw-r--r--
taxonomy.php.php.tar.gz
35.83
KB
-rw-r--r--
taxonomy.php.tar
501
KB
-rw-r--r--
team.html.html.tar.gz
3.06
KB
-rw-r--r--
team.html.tar
15
KB
-rw-r--r--
team.tar
17
KB
-rw-r--r--
team.tar.gz
2.35
KB
-rw-r--r--
team.zip
14.47
KB
-rw-r--r--
team1.jpg.jpg.tar.gz
9.58
KB
-rw-r--r--
team1.jpg.tar
11
KB
-rw-r--r--
team2.jpg.jpg.tar.gz
17.5
KB
-rw-r--r--
team2.jpg.tar
19
KB
-rw-r--r--
team3.jpg.jpg.tar.gz
18.38
KB
-rw-r--r--
team3.jpg.tar
20
KB
-rw-r--r--
template-canvas.php.php.tar.gz
438
B
-rw-r--r--
template-canvas.php.tar
2.5
KB
-rw-r--r--
template-loader.php.php.tar.gz
1.08
KB
-rw-r--r--
template-loader.php.tar
8
KB
-rw-r--r--
template.php.php.tar.gz
4.34
KB
-rw-r--r--
template.php.tar
49
KB
-rw-r--r--
templates.tar
30.33
MB
-rw-r--r--
templates.tar.gz
3.86
MB
-rw-r--r--
templates.zip
20.71
MB
-rw-r--r--
term.php.php.tar.gz
1.05
KB
-rw-r--r--
term.php.tar
13
KB
-rw-r--r--
text-columns.zip
4.2
KB
-rw-r--r--
theme-compat.tar
22.5
KB
-rw-r--r--
theme-compat.tar.gz
4.37
KB
-rw-r--r--
theme-i18n.json.json.tar.gz
501
B
-rw-r--r--
theme-i18n.json.tar
3
KB
-rw-r--r--
theme-install.php.php.tar.gz
5.7
KB
-rw-r--r--
theme-install.php.tar
49
KB
-rw-r--r--
theme-previews.php.php.tar.gz
1.22
KB
-rw-r--r--
theme-previews.php.tar
4.5
KB
-rw-r--r--
theme.json.json.tar.gz
2.26
KB
-rw-r--r--
theme.json.tar
19
KB
-rw-r--r--
themes.tar
167.53
MB
-rw-r--r--
themes.tar.gz
547.87
KB
-rw-r--r--
themes.zip
86.36
MB
-rw-r--r--
thickbox.zip
31.16
KB
-rw-r--r--
thinker.jpg.jpg.tar.gz
225.76
KB
-rw-r--r--
thinker.jpg.tar
228.5
KB
-rw-r--r--
thumb_cart_3.jpg.jpg.tar.gz
603
B
-rw-r--r--
thumb_cart_3.jpg.tar
2.5
KB
-rw-r--r--
tick.png.png.tar.gz
1.49
KB
-rw-r--r--
tick.png.tar
6
KB
-rw-r--r--
tinymce.tar
2.79
MB
-rw-r--r--
tinymce.tar.gz
793.27
KB
-rw-r--r--
tinymce.zip
2.82
MB
-rw-r--r--
tmp.tar
184
KB
-rw-r--r--
tmp.tar.gz
24.86
KB
-rw-r--r--
tmp.zip
177.57
KB
-rw-r--r--
tmpl.tar
4
KB
-rw-r--r--
tmpl.tar.gz
1.04
KB
-rw-r--r--
tools.php.php.tar.gz
1.49
KB
-rw-r--r--
tools.php.tar
13
KB
-rw-r--r--
top_body.png.png.tar.gz
5.81
KB
-rw-r--r--
top_body.png.tar
7.5
KB
-rw-r--r--
twemoji.js.js.tar.gz
8.34
KB
-rw-r--r--
twemoji.js.tar
34
KB
-rw-r--r--
twofactorauth.tar
30.5
KB
-rw-r--r--
twofactorauth.tar.gz
5.57
KB
-rw-r--r--
twofactorauth.zip
25.53
KB
-rw-r--r--
typeahead.js.tar
366
KB
-rw-r--r--
typeahead.js.tar.gz
90.01
KB
-rw-r--r--
typeahead.js.zip
359.79
KB
-rw-r--r--
update-core.php.php.tar.gz
10.55
KB
-rw-r--r--
update-core.php.tar
47
KB
-rw-r--r--
update.php.php.tar.gz
2.72
KB
-rw-r--r--
update.php.tar
65
KB
-rw-r--r--
upgrade-functions.php.php.tar....
321
B
-rw-r--r--
upgrade-functions.php.tar
2
KB
-rw-r--r--
upgrade.php.php.tar.gz
1.96
KB
-rw-r--r--
upgrade.php.tar
14
KB
-rw-r--r--
upload.php.php.tar.gz
4.1
KB
-rw-r--r--
upload.php.tar
16.5
KB
-rw-r--r--
user-edit.php.php.tar.gz
9.74
KB
-rw-r--r--
user-edit.php.tar
41
KB
-rw-r--r--
user-new.php.php.tar.gz
6.29
KB
-rw-r--r--
user-new.php.tar
50
KB
-rw-r--r--
user-resolved-notification.php...
712
B
-rw-r--r--
user-resolved-notification.php...
3
KB
-rw-r--r--
user.php.php.tar.gz
35.39
KB
-rw-r--r--
user.php.tar
195
KB
-rw-r--r--
user.tar
2.64
MB
-rw-r--r--
user.tar.gz
1.06
KB
-rw-r--r--
user.zip
2.53
MB
-rw-r--r--
users.php.php.tar.gz
5.62
KB
-rw-r--r--
users.php.tar
25
KB
-rw-r--r--
utils.zip
185.76
KB
-rw-r--r--
v2.tar
53.5
KB
-rw-r--r--
v2.tar.gz
8.93
KB
-rw-r--r--
v2.zip
45.02
KB
-rw-r--r--
validate.js.js.tar.gz
782
B
-rw-r--r--
validate.js.tar
4.5
KB
-rw-r--r--
vars.php.php.tar.gz
2.04
KB
-rw-r--r--
vars.php.tar
8
KB
-rw-r--r--
vendor.tar
4.32
MB
-rw-r--r--
vendor.tar.gz
129.48
KB
-rw-r--r--
vendor.zip
511.85
KB
-rw-r--r--
verse.zip
2.51
KB
-rw-r--r--
version.php.php.tar.gz
500
B
-rw-r--r--
version.php.tar
6
KB
-rw-r--r--
video.tar
17.71
MB
-rw-r--r--
video.tar.gz
3.62
MB
-rw-r--r--
video.zip
17.71
MB
-rw-r--r--
video_bg.jpg.jpg.tar.gz
154.48
KB
-rw-r--r--
video_bg.jpg.tar
165.5
KB
-rw-r--r--
view.php.php.tar.gz
577
B
-rw-r--r--
view.php.tar
2.5
KB
-rw-r--r--
view.tar
37.5
KB
-rw-r--r--
view.tar.gz
5.48
KB
-rw-r--r--
view.zip
26.48
KB
-rw-r--r--
views.tar
302.5
KB
-rw-r--r--
views.tar.gz
5.61
KB
-rw-r--r--
views.zip
283.46
KB
-rw-r--r--
virtuemart.tar
52
KB
-rw-r--r--
virtuemart.tar.gz
45.17
KB
-rw-r--r--
virtuemart.zip
47.66
KB
-rw-r--r--
vmuserfield.tar
23.5
KB
-rw-r--r--
vmuserfield.tar.gz
5.48
KB
-rw-r--r--
w-logo-blue.png.png.tar.gz
2.56
KB
-rw-r--r--
w-logo-blue.png.tar
5
KB
-rw-r--r--
wartoncorporation.zip
110.63
MB
-rw-r--r--
wcdp_product_settings.php.php....
2.68
KB
-rw-r--r--
wcdp_product_settings.php.tar
12
KB
-rw-r--r--
web.config.txt.config.txt.tar....
721
B
-rw-r--r--
web.config.txt.tar
17
KB
-rw-r--r--
webinstaller.tar
20.5
KB
-rw-r--r--
webinstaller.tar.gz
4.66
KB
-rw-r--r--
webinstaller.zip
44.91
KB
-rw-r--r--
wflogs.tar
2
KB
-rw-r--r--
wflogs.tar.gz
178
B
-rw-r--r--
wflogs.zip
285
B
-rw-r--r--
whatnothing.tar
12.5
KB
-rw-r--r--
whatnothing.tar.gz
10.19
KB
-rw-r--r--
whatnothing.zip
10.72
KB
-rw-r--r--
widget-group.zip
554
B
-rw-r--r--
widgets-form-blocks.php.php.ta...
1.73
KB
-rw-r--r--
widgets-form-blocks.php.tar
6
KB
-rw-r--r--
widgets-form.php.php.tar.gz
5.77
KB
-rw-r--r--
widgets-form.php.tar
21
KB
-rw-r--r--
widgets.php.php.tar.gz
15.36
KB
-rw-r--r--
widgets.php.tar
183
KB
-rw-r--r--
widgets.tar
171
KB
-rw-r--r--
widgets.tar.gz
31.48
KB
-rw-r--r--
widgets.zip
158.12
KB
-rw-r--r--
windows.png.png.tar.gz
1.7
KB
-rw-r--r--
windows.png.tar
3.5
KB
-rw-r--r--
witmmm.php.php.tar.gz
37.96
KB
-rw-r--r--
witmmm.php.tar
54.5
KB
-rw-r--r--
women.png.png.tar.gz
223.27
KB
-rw-r--r--
women.png.tar
225
KB
-rw-r--r--
woocommerce-tm-extra-product-o...
10.22
MB
-rw-r--r--
woocommerce-tm-extra-product-o...
2.21
MB
-rw-r--r--
woocommerce_product_list.tar
36.5
KB
-rw-r--r--
woocommerce_product_list.tar.g...
25.34
KB
-rw-r--r--
woocommerce_product_list.zip
34.45
KB
-rw-r--r--
wordfence.tar
16.01
MB
-rw-r--r--
wordfence.tar.gz
5.66
MB
-rw-r--r--
wordpress.tar
83
KB
-rw-r--r--
wordpress.tar.gz
25.02
KB
-rw-r--r--
wordpress.zip
81.04
KB
-rw-r--r--
wordpress2.png.png.tar.gz
6.84
KB
-rw-r--r--
wordpress2.png.tar
9
KB
-rw-r--r--
workimg3.jpg.jpg.tar.gz
158.56
KB
-rw-r--r--
workimg3.jpg.tar
162
KB
-rw-r--r--
worksec.php
1.06
KB
-rw-r--r--
wow.js.js.tar.gz
3.2
KB
-rw-r--r--
wow.js.tar
15
KB
-rw-r--r--
wp-2fa.tar
2.81
MB
-rw-r--r--
wp-2fa.tar.gz
847.15
KB
-rw-r--r--
wp-activate.php.php.tar.gz
2.55
KB
-rw-r--r--
wp-activate.php.tar
94.5
KB
-rw-r--r--
wp-admin.php.php.tar.gz
11.76
KB
-rw-r--r--
wp-admin.php.tar
35.5
KB
-rw-r--r--
wp-admin.tar
9.64
MB
-rw-r--r--
wp-admin.tar.gz
2.39
MB
-rw-r--r--
wp-admin.zip
8.86
MB
-rw-r--r--
wp-adminx.tar
8.82
MB
-rw-r--r--
wp-adminx.zip
8.5
MB
-rw-r--r--
wp-blog-header.php.php.tar.gz
335
B
-rw-r--r--
wp-blog-header.php.tar
13.5
KB
-rw-r--r--
wp-comments-post.php.php.tar.g...
1.16
KB
-rw-r--r--
wp-comments-post.php.tar
36.5
KB
-rw-r--r--
wp-config-sample.php.php.tar.g...
1.3
KB
-rw-r--r--
wp-config-sample.php.tar
46
KB
-rw-r--r--
wp-config.php.php.tar.gz
1.87
KB
-rw-r--r--
wp-config.php.tar
16
KB
-rw-r--r--
wp-confiig.php.php.tar.gz
1.76
KB
-rw-r--r--
wp-confiig.php.tar
5
KB
-rw-r--r--
wp-conflg.php.php.tar.gz
161
B
-rw-r--r--
wp-conflg.php.tar
2
KB
-rw-r--r--
wp-content.tar
651.58
MB
-rw-r--r--
wp-content.tar.gz
0
B
-rw-r--r--
wp-content.zip
909.19
MB
-rw-r--r--
wp-contentx.tar
9.34
MB
-rw-r--r--
wp-contentx.tar.gz
6.77
MB
-rw-r--r--
wp-contentx.zip
9.13
MB
-rw-r--r--
wp-cron.php.php.tar.gz
2.13
KB
-rw-r--r--
wp-cron.php.tar
66.5
KB
-rw-r--r--
wp-db.php.php.tar.gz
385
B
-rw-r--r--
wp-db.php.tar
3
KB
-rw-r--r--
wp-diff.php.php.tar.gz
402
B
-rw-r--r--
wp-diff.php.tar
2.5
KB
-rw-r--r--
wp-emoji.min.js.min.js.tar.gz
1.51
KB
-rw-r--r--
wp-emoji.min.js.tar
8
KB
-rw-r--r--
wp-includes.tar
51.18
MB
-rw-r--r--
wp-includes.tar.gz
8.7
MB
-rw-r--r--
wp-includes.zip
50.31
MB
-rw-r--r--
wp-includesx.zip
38.31
MB
-rw-r--r--
wp-info.php.php.tar.gz
3.18
KB
-rw-r--r--
wp-info.php.tar
9.5
KB
-rw-r--r--
wp-links-opml.php.php.tar.gz
1.21
KB
-rw-r--r--
wp-links-opml.php.tar
40
KB
-rw-r--r--
wp-load.php.php.tar.gz
1.71
KB
-rw-r--r--
wp-load.php.tar
48
KB
-rw-r--r--
wp-login.php.php.tar.gz
11.28
KB
-rw-r--r--
wp-login.php.tar
528
KB
-rw-r--r--
wp-mail.php.php.tar.gz
3
KB
-rw-r--r--
wp-mail.php.tar
110
KB
-rw-r--r--
wp-opml.php.php.tar.gz
2.94
KB
-rw-r--r--
wp-opml.php.tar
10
KB
-rw-r--r--
wp-reset.zip
583.84
KB
-rw-r--r--
wp-settings.php.php.tar.gz
4.84
KB
-rw-r--r--
wp-settings.php.tar
268.5
KB
-rw-r--r--
wp-signup.php.php.tar.gz
7.19
KB
-rw-r--r--
wp-signup.php.tar
331.5
KB
-rw-r--r--
wp-trackback.php.php.tar.gz
1.91
KB
-rw-r--r--
wp-trackback.php.tar
72.5
KB
-rw-r--r--
wp-ver.php.php.tar.gz
0
B
-rw-r--r--
wp-ver.php.tar
4
KB
-rw-r--r--
wpcf7-recaptcha.zip
131.23
KB
-rw-r--r--
wpicons.png.png.tar.gz
7.04
KB
-rw-r--r--
wpicons.png.tar
8.5
KB
-rw-r--r--
xindex.html.html.tar.gz
1.35
KB
-rw-r--r--
xindex.html.tar
5
KB
-rw-r--r--
xmlrpc.php.php.tar.gz
1.5
KB
-rw-r--r--
xmlrpc.php.tar
40.5
KB
-rw-r--r--
xx.well-known.tar
84
KB
-rw-r--r--
xx.well-known.tar.gz
24.97
KB
-rw-r--r--
xx.well-known.zip
81.16
KB
-rw-r--r--
yprhgrdk.php.php.tar.gz
522
B
-rw-r--r--
yprhgrdk.php.tar
44.5
KB
-rw-r--r--
zenit.tar
82
KB
-rw-r--r--
zenit.tar.gz
24.81
KB
-rw-r--r--
zenit.zip
80.62
KB
-rw-r--r--
Delete
Unzip
Zip
${this.title}
Close
Code Editor : class-wp-xmlrpc-server.php.tar
home/verseaumee/mymusicpromoonline/wp-includes/class-wp-xmlrpc-server.php 0000644 00000637673 15075043674 0023151 0 ustar 00 <?php /** * XML-RPC protocol support for WordPress * * @package WordPress * @subpackage Publishing */ /** * WordPress XMLRPC server implementation. * * Implements compatibility for Blogger API, MetaWeblog API, MovableType, and * pingback. Additional WordPress API for managing comments, pages, posts, * options, etc. * * As of WordPress 3.5.0, XML-RPC is enabled by default. It can be disabled * via the {@see 'xmlrpc_enabled'} filter found in wp_xmlrpc_server::set_is_enabled(). * * @since 1.5.0 * * @see IXR_Server */ class wp_xmlrpc_server extends IXR_Server { /** * Methods. * * @var array */ public $methods; /** * Blog options. * * @var array */ public $blog_options; /** * IXR_Error instance. * * @var IXR_Error */ public $error; /** * Flags that the user authentication has failed in this instance of wp_xmlrpc_server. * * @var bool */ protected $auth_failed = false; /** * Flags that XML-RPC is enabled * * @var bool */ private $is_enabled; /** * Registers all of the XMLRPC methods that XMLRPC server understands. * * Sets up server and method property. Passes XMLRPC * methods through the {@see 'xmlrpc_methods'} filter to allow plugins to extend * or replace XML-RPC methods. * * @since 1.5.0 */ public function __construct() { $this->methods = array( // WordPress API. 'wp.getUsersBlogs' => 'this:wp_getUsersBlogs', 'wp.newPost' => 'this:wp_newPost', 'wp.editPost' => 'this:wp_editPost', 'wp.deletePost' => 'this:wp_deletePost', 'wp.getPost' => 'this:wp_getPost', 'wp.getPosts' => 'this:wp_getPosts', 'wp.newTerm' => 'this:wp_newTerm', 'wp.editTerm' => 'this:wp_editTerm', 'wp.deleteTerm' => 'this:wp_deleteTerm', 'wp.getTerm' => 'this:wp_getTerm', 'wp.getTerms' => 'this:wp_getTerms', 'wp.getTaxonomy' => 'this:wp_getTaxonomy', 'wp.getTaxonomies' => 'this:wp_getTaxonomies', 'wp.getUser' => 'this:wp_getUser', 'wp.getUsers' => 'this:wp_getUsers', 'wp.getProfile' => 'this:wp_getProfile', 'wp.editProfile' => 'this:wp_editProfile', 'wp.getPage' => 'this:wp_getPage', 'wp.getPages' => 'this:wp_getPages', 'wp.newPage' => 'this:wp_newPage', 'wp.deletePage' => 'this:wp_deletePage', 'wp.editPage' => 'this:wp_editPage', 'wp.getPageList' => 'this:wp_getPageList', 'wp.getAuthors' => 'this:wp_getAuthors', 'wp.getCategories' => 'this:mw_getCategories', // Alias. 'wp.getTags' => 'this:wp_getTags', 'wp.newCategory' => 'this:wp_newCategory', 'wp.deleteCategory' => 'this:wp_deleteCategory', 'wp.suggestCategories' => 'this:wp_suggestCategories', 'wp.uploadFile' => 'this:mw_newMediaObject', // Alias. 'wp.deleteFile' => 'this:wp_deletePost', // Alias. 'wp.getCommentCount' => 'this:wp_getCommentCount', 'wp.getPostStatusList' => 'this:wp_getPostStatusList', 'wp.getPageStatusList' => 'this:wp_getPageStatusList', 'wp.getPageTemplates' => 'this:wp_getPageTemplates', 'wp.getOptions' => 'this:wp_getOptions', 'wp.setOptions' => 'this:wp_setOptions', 'wp.getComment' => 'this:wp_getComment', 'wp.getComments' => 'this:wp_getComments', 'wp.deleteComment' => 'this:wp_deleteComment', 'wp.editComment' => 'this:wp_editComment', 'wp.newComment' => 'this:wp_newComment', 'wp.getCommentStatusList' => 'this:wp_getCommentStatusList', 'wp.getMediaItem' => 'this:wp_getMediaItem', 'wp.getMediaLibrary' => 'this:wp_getMediaLibrary', 'wp.getPostFormats' => 'this:wp_getPostFormats', 'wp.getPostType' => 'this:wp_getPostType', 'wp.getPostTypes' => 'this:wp_getPostTypes', 'wp.getRevisions' => 'this:wp_getRevisions', 'wp.restoreRevision' => 'this:wp_restoreRevision', // Blogger API. 'blogger.getUsersBlogs' => 'this:blogger_getUsersBlogs', 'blogger.getUserInfo' => 'this:blogger_getUserInfo', 'blogger.getPost' => 'this:blogger_getPost', 'blogger.getRecentPosts' => 'this:blogger_getRecentPosts', 'blogger.newPost' => 'this:blogger_newPost', 'blogger.editPost' => 'this:blogger_editPost', 'blogger.deletePost' => 'this:blogger_deletePost', // MetaWeblog API (with MT extensions to structs). 'metaWeblog.newPost' => 'this:mw_newPost', 'metaWeblog.editPost' => 'this:mw_editPost', 'metaWeblog.getPost' => 'this:mw_getPost', 'metaWeblog.getRecentPosts' => 'this:mw_getRecentPosts', 'metaWeblog.getCategories' => 'this:mw_getCategories', 'metaWeblog.newMediaObject' => 'this:mw_newMediaObject', // MetaWeblog API aliases for Blogger API. // See http://www.xmlrpc.com/stories/storyReader$2460 'metaWeblog.deletePost' => 'this:blogger_deletePost', 'metaWeblog.getUsersBlogs' => 'this:blogger_getUsersBlogs', // MovableType API. 'mt.getCategoryList' => 'this:mt_getCategoryList', 'mt.getRecentPostTitles' => 'this:mt_getRecentPostTitles', 'mt.getPostCategories' => 'this:mt_getPostCategories', 'mt.setPostCategories' => 'this:mt_setPostCategories', 'mt.supportedMethods' => 'this:mt_supportedMethods', 'mt.supportedTextFilters' => 'this:mt_supportedTextFilters', 'mt.getTrackbackPings' => 'this:mt_getTrackbackPings', 'mt.publishPost' => 'this:mt_publishPost', // Pingback. 'pingback.ping' => 'this:pingback_ping', 'pingback.extensions.getPingbacks' => 'this:pingback_extensions_getPingbacks', 'demo.sayHello' => 'this:sayHello', 'demo.addTwoNumbers' => 'this:addTwoNumbers', ); $this->initialise_blog_option_info(); /** * Filters the methods exposed by the XML-RPC server. * * This filter can be used to add new methods, and remove built-in methods. * * @since 1.5.0 * * @param string[] $methods An array of XML-RPC methods, keyed by their methodName. */ $this->methods = apply_filters( 'xmlrpc_methods', $this->methods ); $this->set_is_enabled(); } /** * Set wp_xmlrpc_server::$is_enabled property. * * Determine whether the xmlrpc server is enabled on this WordPress install * and set the is_enabled property accordingly. * * @since 5.7.3 */ private function set_is_enabled() { /* * Respect old get_option() filters left for back-compat when the 'enable_xmlrpc' * option was deprecated in 3.5.0. Use the 'xmlrpc_enabled' hook instead. */ $is_enabled = apply_filters( 'pre_option_enable_xmlrpc', false ); if ( false === $is_enabled ) { $is_enabled = apply_filters( 'option_enable_xmlrpc', true ); } /** * Filters whether XML-RPC methods requiring authentication are enabled. * * Contrary to the way it's named, this filter does not control whether XML-RPC is *fully* * enabled, rather, it only controls whether XML-RPC methods requiring authentication - such * as for publishing purposes - are enabled. * * Further, the filter does not control whether pingbacks or other custom endpoints that don't * require authentication are enabled. This behavior is expected, and due to how parity was matched * with the `enable_xmlrpc` UI option the filter replaced when it was introduced in 3.5. * * To disable XML-RPC methods that require authentication, use: * * add_filter( 'xmlrpc_enabled', '__return_false' ); * * For more granular control over all XML-RPC methods and requests, see the {@see 'xmlrpc_methods'} * and {@see 'xmlrpc_element_limit'} hooks. * * @since 3.5.0 * * @param bool $is_enabled Whether XML-RPC is enabled. Default true. */ $this->is_enabled = apply_filters( 'xmlrpc_enabled', $is_enabled ); } /** * Make private/protected methods readable for backward compatibility. * * @since 4.0.0 * * @param string $name Method to call. * @param array $arguments Arguments to pass when calling. * @return array|IXR_Error|false Return value of the callback, false otherwise. */ public function __call( $name, $arguments ) { if ( '_multisite_getUsersBlogs' === $name ) { return $this->_multisite_getUsersBlogs( ...$arguments ); } return false; } /** * Serves the XML-RPC request. * * @since 2.9.0 */ public function serve_request() { $this->IXR_Server( $this->methods ); } /** * Test XMLRPC API by saying, "Hello!" to client. * * @since 1.5.0 * * @return string Hello string response. */ public function sayHello() { return 'Hello!'; } /** * Test XMLRPC API by adding two numbers for client. * * @since 1.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $number1 A number to add. * @type int $number2 A second number to add. * } * @return int Sum of the two given numbers. */ public function addTwoNumbers( $args ) { $number1 = $args[0]; $number2 = $args[1]; return $number1 + $number2; } /** * Log user in. * * @since 2.8.0 * * @param string $username User's username. * @param string $password User's password. * @return WP_User|false WP_User object if authentication passed, false otherwise */ public function login( $username, $password ) { if ( ! $this->is_enabled ) { $this->error = new IXR_Error( 405, sprintf( __( 'XML-RPC services are disabled on this site.' ) ) ); return false; } if ( $this->auth_failed ) { $user = new WP_Error( 'login_prevented' ); } else { $user = wp_authenticate( $username, $password ); } if ( is_wp_error( $user ) ) { $this->error = new IXR_Error( 403, __( 'Incorrect username or password.' ) ); // Flag that authentication has failed once on this wp_xmlrpc_server instance. $this->auth_failed = true; /** * Filters the XML-RPC user login error message. * * @since 3.5.0 * * @param IXR_Error $error The XML-RPC error message. * @param WP_Error $user WP_Error object. */ $this->error = apply_filters( 'xmlrpc_login_error', $this->error, $user ); return false; } wp_set_current_user( $user->ID ); return $user; } /** * Check user's credentials. Deprecated. * * @since 1.5.0 * @deprecated 2.8.0 Use wp_xmlrpc_server::login() * @see wp_xmlrpc_server::login() * * @param string $username User's username. * @param string $password User's password. * @return bool Whether authentication passed. */ public function login_pass_ok( $username, $password ) { return (bool) $this->login( $username, $password ); } /** * Escape string or array of strings for database. * * @since 1.5.2 * * @param string|array $data Escape single string or array of strings. * @return string|void Returns with string is passed, alters by-reference * when array is passed. */ public function escape( &$data ) { if ( ! is_array( $data ) ) { return wp_slash( $data ); } foreach ( $data as &$v ) { if ( is_array( $v ) ) { $this->escape( $v ); } elseif ( ! is_object( $v ) ) { $v = wp_slash( $v ); } } } /** * Send error response to client. * * Send an XML error response to the client. If the endpoint is enabled * an HTTP 200 response is always sent per the XML-RPC specification. * * @since 5.7.3 * * @param IXR_Error|string $error Error code or an error object. * @param false $message Error message. Optional. */ public function error( $error, $message = false ) { // Accepts either an error object or an error code and message if ( $message && ! is_object( $error ) ) { $error = new IXR_Error( $error, $message ); } if ( ! $this->is_enabled ) { status_header( $error->code ); } $this->output( $error->getXml() ); } /** * Retrieve custom fields for post. * * @since 2.5.0 * * @param int $post_id Post ID. * @return array Custom fields, if exist. */ public function get_custom_fields( $post_id ) { $post_id = (int) $post_id; $custom_fields = array(); foreach ( (array) has_meta( $post_id ) as $meta ) { // Don't expose protected fields. if ( ! current_user_can( 'edit_post_meta', $post_id, $meta['meta_key'] ) ) { continue; } $custom_fields[] = array( 'id' => $meta['meta_id'], 'key' => $meta['meta_key'], 'value' => $meta['meta_value'], ); } return $custom_fields; } /** * Set custom fields for post. * * @since 2.5.0 * * @param int $post_id Post ID. * @param array $fields Custom fields. */ public function set_custom_fields( $post_id, $fields ) { $post_id = (int) $post_id; foreach ( (array) $fields as $meta ) { if ( isset( $meta['id'] ) ) { $meta['id'] = (int) $meta['id']; $pmeta = get_metadata_by_mid( 'post', $meta['id'] ); if ( ! $pmeta || $pmeta->post_id != $post_id ) { continue; } if ( isset( $meta['key'] ) ) { $meta['key'] = wp_unslash( $meta['key'] ); if ( $meta['key'] !== $pmeta->meta_key ) { continue; } $meta['value'] = wp_unslash( $meta['value'] ); if ( current_user_can( 'edit_post_meta', $post_id, $meta['key'] ) ) { update_metadata_by_mid( 'post', $meta['id'], $meta['value'] ); } } elseif ( current_user_can( 'delete_post_meta', $post_id, $pmeta->meta_key ) ) { delete_metadata_by_mid( 'post', $meta['id'] ); } } elseif ( current_user_can( 'add_post_meta', $post_id, wp_unslash( $meta['key'] ) ) ) { add_post_meta( $post_id, $meta['key'], $meta['value'] ); } } } /** * Retrieve custom fields for a term. * * @since 4.9.0 * * @param int $term_id Term ID. * @return array Array of custom fields, if they exist. */ public function get_term_custom_fields( $term_id ) { $term_id = (int) $term_id; $custom_fields = array(); foreach ( (array) has_term_meta( $term_id ) as $meta ) { if ( ! current_user_can( 'edit_term_meta', $term_id ) ) { continue; } $custom_fields[] = array( 'id' => $meta['meta_id'], 'key' => $meta['meta_key'], 'value' => $meta['meta_value'], ); } return $custom_fields; } /** * Set custom fields for a term. * * @since 4.9.0 * * @param int $term_id Term ID. * @param array $fields Custom fields. */ public function set_term_custom_fields( $term_id, $fields ) { $term_id = (int) $term_id; foreach ( (array) $fields as $meta ) { if ( isset( $meta['id'] ) ) { $meta['id'] = (int) $meta['id']; $pmeta = get_metadata_by_mid( 'term', $meta['id'] ); if ( isset( $meta['key'] ) ) { $meta['key'] = wp_unslash( $meta['key'] ); if ( $meta['key'] !== $pmeta->meta_key ) { continue; } $meta['value'] = wp_unslash( $meta['value'] ); if ( current_user_can( 'edit_term_meta', $term_id ) ) { update_metadata_by_mid( 'term', $meta['id'], $meta['value'] ); } } elseif ( current_user_can( 'delete_term_meta', $term_id ) ) { delete_metadata_by_mid( 'term', $meta['id'] ); } } elseif ( current_user_can( 'add_term_meta', $term_id ) ) { add_term_meta( $term_id, $meta['key'], $meta['value'] ); } } } /** * Set up blog options property. * * Passes property through {@see 'xmlrpc_blog_options'} filter. * * @since 2.6.0 */ public function initialise_blog_option_info() { $this->blog_options = array( // Read-only options. 'software_name' => array( 'desc' => __( 'Software Name' ), 'readonly' => true, 'value' => 'WordPress', ), 'software_version' => array( 'desc' => __( 'Software Version' ), 'readonly' => true, 'value' => get_bloginfo( 'version' ), ), 'blog_url' => array( 'desc' => __( 'WordPress Address (URL)' ), 'readonly' => true, 'option' => 'siteurl', ), 'home_url' => array( 'desc' => __( 'Site Address (URL)' ), 'readonly' => true, 'option' => 'home', ), 'login_url' => array( 'desc' => __( 'Login Address (URL)' ), 'readonly' => true, 'value' => wp_login_url(), ), 'admin_url' => array( 'desc' => __( 'The URL to the admin area' ), 'readonly' => true, 'value' => get_admin_url(), ), 'image_default_link_type' => array( 'desc' => __( 'Image default link type' ), 'readonly' => true, 'option' => 'image_default_link_type', ), 'image_default_size' => array( 'desc' => __( 'Image default size' ), 'readonly' => true, 'option' => 'image_default_size', ), 'image_default_align' => array( 'desc' => __( 'Image default align' ), 'readonly' => true, 'option' => 'image_default_align', ), 'template' => array( 'desc' => __( 'Template' ), 'readonly' => true, 'option' => 'template', ), 'stylesheet' => array( 'desc' => __( 'Stylesheet' ), 'readonly' => true, 'option' => 'stylesheet', ), 'post_thumbnail' => array( 'desc' => __( 'Post Thumbnail' ), 'readonly' => true, 'value' => current_theme_supports( 'post-thumbnails' ), ), // Updatable options. 'time_zone' => array( 'desc' => __( 'Time Zone' ), 'readonly' => false, 'option' => 'gmt_offset', ), 'blog_title' => array( 'desc' => __( 'Site Title' ), 'readonly' => false, 'option' => 'blogname', ), 'blog_tagline' => array( 'desc' => __( 'Site Tagline' ), 'readonly' => false, 'option' => 'blogdescription', ), 'date_format' => array( 'desc' => __( 'Date Format' ), 'readonly' => false, 'option' => 'date_format', ), 'time_format' => array( 'desc' => __( 'Time Format' ), 'readonly' => false, 'option' => 'time_format', ), 'users_can_register' => array( 'desc' => __( 'Allow new users to sign up' ), 'readonly' => false, 'option' => 'users_can_register', ), 'thumbnail_size_w' => array( 'desc' => __( 'Thumbnail Width' ), 'readonly' => false, 'option' => 'thumbnail_size_w', ), 'thumbnail_size_h' => array( 'desc' => __( 'Thumbnail Height' ), 'readonly' => false, 'option' => 'thumbnail_size_h', ), 'thumbnail_crop' => array( 'desc' => __( 'Crop thumbnail to exact dimensions' ), 'readonly' => false, 'option' => 'thumbnail_crop', ), 'medium_size_w' => array( 'desc' => __( 'Medium size image width' ), 'readonly' => false, 'option' => 'medium_size_w', ), 'medium_size_h' => array( 'desc' => __( 'Medium size image height' ), 'readonly' => false, 'option' => 'medium_size_h', ), 'medium_large_size_w' => array( 'desc' => __( 'Medium-Large size image width' ), 'readonly' => false, 'option' => 'medium_large_size_w', ), 'medium_large_size_h' => array( 'desc' => __( 'Medium-Large size image height' ), 'readonly' => false, 'option' => 'medium_large_size_h', ), 'large_size_w' => array( 'desc' => __( 'Large size image width' ), 'readonly' => false, 'option' => 'large_size_w', ), 'large_size_h' => array( 'desc' => __( 'Large size image height' ), 'readonly' => false, 'option' => 'large_size_h', ), 'default_comment_status' => array( 'desc' => __( 'Allow people to submit comments on new posts.' ), 'readonly' => false, 'option' => 'default_comment_status', ), 'default_ping_status' => array( 'desc' => __( 'Allow link notifications from other blogs (pingbacks and trackbacks) on new posts.' ), 'readonly' => false, 'option' => 'default_ping_status', ), ); /** * Filters the XML-RPC blog options property. * * @since 2.6.0 * * @param array $blog_options An array of XML-RPC blog options. */ $this->blog_options = apply_filters( 'xmlrpc_blog_options', $this->blog_options ); } /** * Retrieve the blogs of the user. * * @since 2.6.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type string $username Username. * @type string $password Password. * } * @return array|IXR_Error Array contains: * - 'isAdmin' * - 'isPrimary' - whether the blog is the user's primary blog * - 'url' * - 'blogid' * - 'blogName' * - 'xmlrpc' - url of xmlrpc endpoint */ public function wp_getUsersBlogs( $args ) { if ( ! $this->minimum_args( $args, 2 ) ) { return $this->error; } // If this isn't on WPMU then just use blogger_getUsersBlogs(). if ( ! is_multisite() ) { array_unshift( $args, 1 ); return $this->blogger_getUsersBlogs( $args ); } $this->escape( $args ); $username = $args[0]; $password = $args[1]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** * Fires after the XML-RPC user has been authenticated but before the rest of * the method logic begins. * * All built-in XML-RPC methods use the action xmlrpc_call, with a parameter * equal to the method's name, e.g., wp.getUsersBlogs, wp.newPost, etc. * * @since 2.5.0 * @since 5.7.0 Added the `$args` and `$server` parameters. * * @param string $name The method name. * @param array|string $args The escaped arguments passed to the method. * @param wp_xmlrpc_server $server The XML-RPC server instance. */ do_action( 'xmlrpc_call', 'wp.getUsersBlogs', $args, $this ); $blogs = (array) get_blogs_of_user( $user->ID ); $struct = array(); $primary_blog_id = 0; $active_blog = get_active_blog_for_user( $user->ID ); if ( $active_blog ) { $primary_blog_id = (int) $active_blog->blog_id; } foreach ( $blogs as $blog ) { // Don't include blogs that aren't hosted at this site. if ( get_current_network_id() != $blog->site_id ) { continue; } $blog_id = $blog->userblog_id; switch_to_blog( $blog_id ); $is_admin = current_user_can( 'manage_options' ); $is_primary = ( (int) $blog_id === $primary_blog_id ); $struct[] = array( 'isAdmin' => $is_admin, 'isPrimary' => $is_primary, 'url' => home_url( '/' ), 'blogid' => (string) $blog_id, 'blogName' => get_option( 'blogname' ), 'xmlrpc' => site_url( 'xmlrpc.php', 'rpc' ), ); restore_current_blog(); } return $struct; } /** * Checks if the method received at least the minimum number of arguments. * * @since 3.4.0 * * @param array $args An array of arguments to check. * @param int $count Minimum number of arguments. * @return bool True if `$args` contains at least `$count` arguments, false otherwise. */ protected function minimum_args( $args, $count ) { if ( ! is_array( $args ) || count( $args ) < $count ) { $this->error = new IXR_Error( 400, __( 'Insufficient arguments passed to this XML-RPC method.' ) ); return false; } return true; } /** * Prepares taxonomy data for return in an XML-RPC object. * * @param WP_Taxonomy $taxonomy The unprepared taxonomy data. * @param array $fields The subset of taxonomy fields to return. * @return array The prepared taxonomy data. */ protected function _prepare_taxonomy( $taxonomy, $fields ) { $_taxonomy = array( 'name' => $taxonomy->name, 'label' => $taxonomy->label, 'hierarchical' => (bool) $taxonomy->hierarchical, 'public' => (bool) $taxonomy->public, 'show_ui' => (bool) $taxonomy->show_ui, '_builtin' => (bool) $taxonomy->_builtin, ); if ( in_array( 'labels', $fields, true ) ) { $_taxonomy['labels'] = (array) $taxonomy->labels; } if ( in_array( 'cap', $fields, true ) ) { $_taxonomy['cap'] = (array) $taxonomy->cap; } if ( in_array( 'menu', $fields, true ) ) { $_taxonomy['show_in_menu'] = (bool) $taxonomy->show_in_menu; } if ( in_array( 'object_type', $fields, true ) ) { $_taxonomy['object_type'] = array_unique( (array) $taxonomy->object_type ); } /** * Filters XML-RPC-prepared data for the given taxonomy. * * @since 3.4.0 * * @param array $_taxonomy An array of taxonomy data. * @param WP_Taxonomy $taxonomy Taxonomy object. * @param array $fields The subset of taxonomy fields to return. */ return apply_filters( 'xmlrpc_prepare_taxonomy', $_taxonomy, $taxonomy, $fields ); } /** * Prepares term data for return in an XML-RPC object. * * @param array|object $term The unprepared term data. * @return array The prepared term data. */ protected function _prepare_term( $term ) { $_term = $term; if ( ! is_array( $_term ) ) { $_term = get_object_vars( $_term ); } // For integers which may be larger than XML-RPC supports ensure we return strings. $_term['term_id'] = (string) $_term['term_id']; $_term['term_group'] = (string) $_term['term_group']; $_term['term_taxonomy_id'] = (string) $_term['term_taxonomy_id']; $_term['parent'] = (string) $_term['parent']; // Count we are happy to return as an integer because people really shouldn't use terms that much. $_term['count'] = (int) $_term['count']; // Get term meta. $_term['custom_fields'] = $this->get_term_custom_fields( $_term['term_id'] ); /** * Filters XML-RPC-prepared data for the given term. * * @since 3.4.0 * * @param array $_term An array of term data. * @param array|object $term Term object or array. */ return apply_filters( 'xmlrpc_prepare_term', $_term, $term ); } /** * Convert a WordPress date string to an IXR_Date object. * * @param string $date Date string to convert. * @return IXR_Date IXR_Date object. */ protected function _convert_date( $date ) { if ( '0000-00-00 00:00:00' === $date ) { return new IXR_Date( '00000000T00:00:00Z' ); } return new IXR_Date( mysql2date( 'Ymd\TH:i:s', $date, false ) ); } /** * Convert a WordPress GMT date string to an IXR_Date object. * * @param string $date_gmt WordPress GMT date string. * @param string $date Date string. * @return IXR_Date IXR_Date object. */ protected function _convert_date_gmt( $date_gmt, $date ) { if ( '0000-00-00 00:00:00' !== $date && '0000-00-00 00:00:00' === $date_gmt ) { return new IXR_Date( get_gmt_from_date( mysql2date( 'Y-m-d H:i:s', $date, false ), 'Ymd\TH:i:s' ) ); } return $this->_convert_date( $date_gmt ); } /** * Prepares post data for return in an XML-RPC object. * * @param array $post The unprepared post data. * @param array $fields The subset of post type fields to return. * @return array The prepared post data. */ protected function _prepare_post( $post, $fields ) { // Holds the data for this post. built up based on $fields. $_post = array( 'post_id' => (string) $post['ID'] ); // Prepare common post fields. $post_fields = array( 'post_title' => $post['post_title'], 'post_date' => $this->_convert_date( $post['post_date'] ), 'post_date_gmt' => $this->_convert_date_gmt( $post['post_date_gmt'], $post['post_date'] ), 'post_modified' => $this->_convert_date( $post['post_modified'] ), 'post_modified_gmt' => $this->_convert_date_gmt( $post['post_modified_gmt'], $post['post_modified'] ), 'post_status' => $post['post_status'], 'post_type' => $post['post_type'], 'post_name' => $post['post_name'], 'post_author' => $post['post_author'], 'post_password' => $post['post_password'], 'post_excerpt' => $post['post_excerpt'], 'post_content' => $post['post_content'], 'post_parent' => (string) $post['post_parent'], 'post_mime_type' => $post['post_mime_type'], 'link' => get_permalink( $post['ID'] ), 'guid' => $post['guid'], 'menu_order' => (int) $post['menu_order'], 'comment_status' => $post['comment_status'], 'ping_status' => $post['ping_status'], 'sticky' => ( 'post' === $post['post_type'] && is_sticky( $post['ID'] ) ), ); // Thumbnail. $post_fields['post_thumbnail'] = array(); $thumbnail_id = get_post_thumbnail_id( $post['ID'] ); if ( $thumbnail_id ) { $thumbnail_size = current_theme_supports( 'post-thumbnail' ) ? 'post-thumbnail' : 'thumbnail'; $post_fields['post_thumbnail'] = $this->_prepare_media_item( get_post( $thumbnail_id ), $thumbnail_size ); } // Consider future posts as published. if ( 'future' === $post_fields['post_status'] ) { $post_fields['post_status'] = 'publish'; } // Fill in blank post format. $post_fields['post_format'] = get_post_format( $post['ID'] ); if ( empty( $post_fields['post_format'] ) ) { $post_fields['post_format'] = 'standard'; } // Merge requested $post_fields fields into $_post. if ( in_array( 'post', $fields, true ) ) { $_post = array_merge( $_post, $post_fields ); } else { $requested_fields = array_intersect_key( $post_fields, array_flip( $fields ) ); $_post = array_merge( $_post, $requested_fields ); } $all_taxonomy_fields = in_array( 'taxonomies', $fields, true ); if ( $all_taxonomy_fields || in_array( 'terms', $fields, true ) ) { $post_type_taxonomies = get_object_taxonomies( $post['post_type'], 'names' ); $terms = wp_get_object_terms( $post['ID'], $post_type_taxonomies ); $_post['terms'] = array(); foreach ( $terms as $term ) { $_post['terms'][] = $this->_prepare_term( $term ); } } if ( in_array( 'custom_fields', $fields, true ) ) { $_post['custom_fields'] = $this->get_custom_fields( $post['ID'] ); } if ( in_array( 'enclosure', $fields, true ) ) { $_post['enclosure'] = array(); $enclosures = (array) get_post_meta( $post['ID'], 'enclosure' ); if ( ! empty( $enclosures ) ) { $encdata = explode( "\n", $enclosures[0] ); $_post['enclosure']['url'] = trim( htmlspecialchars( $encdata[0] ) ); $_post['enclosure']['length'] = (int) trim( $encdata[1] ); $_post['enclosure']['type'] = trim( $encdata[2] ); } } /** * Filters XML-RPC-prepared date for the given post. * * @since 3.4.0 * * @param array $_post An array of modified post data. * @param array $post An array of post data. * @param array $fields An array of post fields. */ return apply_filters( 'xmlrpc_prepare_post', $_post, $post, $fields ); } /** * Prepares post data for return in an XML-RPC object. * * @since 3.4.0 * @since 4.6.0 Converted the `$post_type` parameter to accept a WP_Post_Type object. * * @param WP_Post_Type $post_type Post type object. * @param array $fields The subset of post fields to return. * @return array The prepared post type data. */ protected function _prepare_post_type( $post_type, $fields ) { $_post_type = array( 'name' => $post_type->name, 'label' => $post_type->label, 'hierarchical' => (bool) $post_type->hierarchical, 'public' => (bool) $post_type->public, 'show_ui' => (bool) $post_type->show_ui, '_builtin' => (bool) $post_type->_builtin, 'has_archive' => (bool) $post_type->has_archive, 'supports' => get_all_post_type_supports( $post_type->name ), ); if ( in_array( 'labels', $fields, true ) ) { $_post_type['labels'] = (array) $post_type->labels; } if ( in_array( 'cap', $fields, true ) ) { $_post_type['cap'] = (array) $post_type->cap; $_post_type['map_meta_cap'] = (bool) $post_type->map_meta_cap; } if ( in_array( 'menu', $fields, true ) ) { $_post_type['menu_position'] = (int) $post_type->menu_position; $_post_type['menu_icon'] = $post_type->menu_icon; $_post_type['show_in_menu'] = (bool) $post_type->show_in_menu; } if ( in_array( 'taxonomies', $fields, true ) ) { $_post_type['taxonomies'] = get_object_taxonomies( $post_type->name, 'names' ); } /** * Filters XML-RPC-prepared date for the given post type. * * @since 3.4.0 * @since 4.6.0 Converted the `$post_type` parameter to accept a WP_Post_Type object. * * @param array $_post_type An array of post type data. * @param WP_Post_Type $post_type Post type object. */ return apply_filters( 'xmlrpc_prepare_post_type', $_post_type, $post_type ); } /** * Prepares media item data for return in an XML-RPC object. * * @param WP_Post $media_item The unprepared media item data. * @param string $thumbnail_size The image size to use for the thumbnail URL. * @return array The prepared media item data. */ protected function _prepare_media_item( $media_item, $thumbnail_size = 'thumbnail' ) { $_media_item = array( 'attachment_id' => (string) $media_item->ID, 'date_created_gmt' => $this->_convert_date_gmt( $media_item->post_date_gmt, $media_item->post_date ), 'parent' => $media_item->post_parent, 'link' => wp_get_attachment_url( $media_item->ID ), 'title' => $media_item->post_title, 'caption' => $media_item->post_excerpt, 'description' => $media_item->post_content, 'metadata' => wp_get_attachment_metadata( $media_item->ID ), 'type' => $media_item->post_mime_type, ); $thumbnail_src = image_downsize( $media_item->ID, $thumbnail_size ); if ( $thumbnail_src ) { $_media_item['thumbnail'] = $thumbnail_src[0]; } else { $_media_item['thumbnail'] = $_media_item['link']; } /** * Filters XML-RPC-prepared data for the given media item. * * @since 3.4.0 * * @param array $_media_item An array of media item data. * @param WP_Post $media_item Media item object. * @param string $thumbnail_size Image size. */ return apply_filters( 'xmlrpc_prepare_media_item', $_media_item, $media_item, $thumbnail_size ); } /** * Prepares page data for return in an XML-RPC object. * * @param WP_Post $page The unprepared page data. * @return array The prepared page data. */ protected function _prepare_page( $page ) { // Get all of the page content and link. $full_page = get_extended( $page->post_content ); $link = get_permalink( $page->ID ); // Get info the page parent if there is one. $parent_title = ''; if ( ! empty( $page->post_parent ) ) { $parent = get_post( $page->post_parent ); $parent_title = $parent->post_title; } // Determine comment and ping settings. $allow_comments = comments_open( $page->ID ) ? 1 : 0; $allow_pings = pings_open( $page->ID ) ? 1 : 0; // Format page date. $page_date = $this->_convert_date( $page->post_date ); $page_date_gmt = $this->_convert_date_gmt( $page->post_date_gmt, $page->post_date ); // Pull the categories info together. $categories = array(); if ( is_object_in_taxonomy( 'page', 'category' ) ) { foreach ( wp_get_post_categories( $page->ID ) as $cat_id ) { $categories[] = get_cat_name( $cat_id ); } } // Get the author info. $author = get_userdata( $page->post_author ); $page_template = get_page_template_slug( $page->ID ); if ( empty( $page_template ) ) { $page_template = 'default'; } $_page = array( 'dateCreated' => $page_date, 'userid' => $page->post_author, 'page_id' => $page->ID, 'page_status' => $page->post_status, 'description' => $full_page['main'], 'title' => $page->post_title, 'link' => $link, 'permaLink' => $link, 'categories' => $categories, 'excerpt' => $page->post_excerpt, 'text_more' => $full_page['extended'], 'mt_allow_comments' => $allow_comments, 'mt_allow_pings' => $allow_pings, 'wp_slug' => $page->post_name, 'wp_password' => $page->post_password, 'wp_author' => $author->display_name, 'wp_page_parent_id' => $page->post_parent, 'wp_page_parent_title' => $parent_title, 'wp_page_order' => $page->menu_order, 'wp_author_id' => (string) $author->ID, 'wp_author_display_name' => $author->display_name, 'date_created_gmt' => $page_date_gmt, 'custom_fields' => $this->get_custom_fields( $page->ID ), 'wp_page_template' => $page_template, ); /** * Filters XML-RPC-prepared data for the given page. * * @since 3.4.0 * * @param array $_page An array of page data. * @param WP_Post $page Page object. */ return apply_filters( 'xmlrpc_prepare_page', $_page, $page ); } /** * Prepares comment data for return in an XML-RPC object. * * @param WP_Comment $comment The unprepared comment data. * @return array The prepared comment data. */ protected function _prepare_comment( $comment ) { // Format page date. $comment_date_gmt = $this->_convert_date_gmt( $comment->comment_date_gmt, $comment->comment_date ); if ( '0' == $comment->comment_approved ) { $comment_status = 'hold'; } elseif ( 'spam' === $comment->comment_approved ) { $comment_status = 'spam'; } elseif ( '1' == $comment->comment_approved ) { $comment_status = 'approve'; } else { $comment_status = $comment->comment_approved; } $_comment = array( 'date_created_gmt' => $comment_date_gmt, 'user_id' => $comment->user_id, 'comment_id' => $comment->comment_ID, 'parent' => $comment->comment_parent, 'status' => $comment_status, 'content' => $comment->comment_content, 'link' => get_comment_link( $comment ), 'post_id' => $comment->comment_post_ID, 'post_title' => get_the_title( $comment->comment_post_ID ), 'author' => $comment->comment_author, 'author_url' => $comment->comment_author_url, 'author_email' => $comment->comment_author_email, 'author_ip' => $comment->comment_author_IP, 'type' => $comment->comment_type, ); /** * Filters XML-RPC-prepared data for the given comment. * * @since 3.4.0 * * @param array $_comment An array of prepared comment data. * @param WP_Comment $comment Comment object. */ return apply_filters( 'xmlrpc_prepare_comment', $_comment, $comment ); } /** * Prepares user data for return in an XML-RPC object. * * @param WP_User $user The unprepared user object. * @param array $fields The subset of user fields to return. * @return array The prepared user data. */ protected function _prepare_user( $user, $fields ) { $_user = array( 'user_id' => (string) $user->ID ); $user_fields = array( 'username' => $user->user_login, 'first_name' => $user->user_firstname, 'last_name' => $user->user_lastname, 'registered' => $this->_convert_date( $user->user_registered ), 'bio' => $user->user_description, 'email' => $user->user_email, 'nickname' => $user->nickname, 'nicename' => $user->user_nicename, 'url' => $user->user_url, 'display_name' => $user->display_name, 'roles' => $user->roles, ); if ( in_array( 'all', $fields, true ) ) { $_user = array_merge( $_user, $user_fields ); } else { if ( in_array( 'basic', $fields, true ) ) { $basic_fields = array( 'username', 'email', 'registered', 'display_name', 'nicename' ); $fields = array_merge( $fields, $basic_fields ); } $requested_fields = array_intersect_key( $user_fields, array_flip( $fields ) ); $_user = array_merge( $_user, $requested_fields ); } /** * Filters XML-RPC-prepared data for the given user. * * @since 3.5.0 * * @param array $_user An array of user data. * @param WP_User $user User object. * @param array $fields An array of user fields. */ return apply_filters( 'xmlrpc_prepare_user', $_user, $user, $fields ); } /** * Create a new post for any registered post type. * * @since 3.4.0 * * @link https://en.wikipedia.org/wiki/RSS_enclosure for information on RSS enclosures. * * @param array $args { * Method arguments. Note: top-level arguments must be ordered as documented. * * @type int $blog_id Blog ID (unused). * @type string $username Username. * @type string $password Password. * @type array $content_struct { * Content struct for adding a new post. See wp_insert_post() for information on * additional post fields * * @type string $post_type Post type. Default 'post'. * @type string $post_status Post status. Default 'draft' * @type string $post_title Post title. * @type int $post_author Post author ID. * @type string $post_excerpt Post excerpt. * @type string $post_content Post content. * @type string $post_date_gmt Post date in GMT. * @type string $post_date Post date. * @type string $post_password Post password (20-character limit). * @type string $comment_status Post comment enabled status. Accepts 'open' or 'closed'. * @type string $ping_status Post ping status. Accepts 'open' or 'closed'. * @type bool $sticky Whether the post should be sticky. Automatically false if * `$post_status` is 'private'. * @type int $post_thumbnail ID of an image to use as the post thumbnail/featured image. * @type array $custom_fields Array of meta key/value pairs to add to the post. * @type array $terms Associative array with taxonomy names as keys and arrays * of term IDs as values. * @type array $terms_names Associative array with taxonomy names as keys and arrays * of term names as values. * @type array $enclosure { * Array of feed enclosure data to add to post meta. * * @type string $url URL for the feed enclosure. * @type int $length Size in bytes of the enclosure. * @type string $type Mime-type for the enclosure. * } * } * } * @return int|IXR_Error Post ID on success, IXR_Error instance otherwise. */ public function wp_newPost( $args ) { if ( ! $this->minimum_args( $args, 4 ) ) { return $this->error; } $this->escape( $args ); $username = $args[1]; $password = $args[2]; $content_struct = $args[3]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } // Convert the date field back to IXR form. if ( isset( $content_struct['post_date'] ) && ! ( $content_struct['post_date'] instanceof IXR_Date ) ) { $content_struct['post_date'] = $this->_convert_date( $content_struct['post_date'] ); } /* * Ignore the existing GMT date if it is empty or a non-GMT date was supplied in $content_struct, * since _insert_post() will ignore the non-GMT date if the GMT date is set. */ if ( isset( $content_struct['post_date_gmt'] ) && ! ( $content_struct['post_date_gmt'] instanceof IXR_Date ) ) { if ( '0000-00-00 00:00:00' === $content_struct['post_date_gmt'] || isset( $content_struct['post_date'] ) ) { unset( $content_struct['post_date_gmt'] ); } else { $content_struct['post_date_gmt'] = $this->_convert_date( $content_struct['post_date_gmt'] ); } } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.newPost', $args, $this ); unset( $content_struct['ID'] ); return $this->_insert_post( $user, $content_struct ); } /** * Helper method for filtering out elements from an array. * * @since 3.4.0 * * @param int $count Number to compare to one. * @return bool True if the number is greater than one, false otherwise. */ private function _is_greater_than_one( $count ) { return $count > 1; } /** * Encapsulate the logic for sticking a post * and determining if the user has permission to do so * * @since 4.3.0 * * @param array $post_data * @param bool $update * @return void|IXR_Error */ private function _toggle_sticky( $post_data, $update = false ) { $post_type = get_post_type_object( $post_data['post_type'] ); // Private and password-protected posts cannot be stickied. if ( 'private' === $post_data['post_status'] || ! empty( $post_data['post_password'] ) ) { // Error if the client tried to stick the post, otherwise, silently unstick. if ( ! empty( $post_data['sticky'] ) ) { return new IXR_Error( 401, __( 'Sorry, you cannot stick a private post.' ) ); } if ( $update ) { unstick_post( $post_data['ID'] ); } } elseif ( isset( $post_data['sticky'] ) ) { if ( ! current_user_can( $post_type->cap->edit_others_posts ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to make posts sticky.' ) ); } $sticky = wp_validate_boolean( $post_data['sticky'] ); if ( $sticky ) { stick_post( $post_data['ID'] ); } else { unstick_post( $post_data['ID'] ); } } } /** * Helper method for wp_newPost() and wp_editPost(), containing shared logic. * * @since 3.4.0 * * @see wp_insert_post() * * @param WP_User $user The post author if post_author isn't set in $content_struct. * @param array|IXR_Error $content_struct Post data to insert. * @return IXR_Error|string */ protected function _insert_post( $user, $content_struct ) { $defaults = array( 'post_status' => 'draft', 'post_type' => 'post', 'post_author' => null, 'post_password' => null, 'post_excerpt' => null, 'post_content' => null, 'post_title' => null, 'post_date' => null, 'post_date_gmt' => null, 'post_format' => null, 'post_name' => null, 'post_thumbnail' => null, 'post_parent' => null, 'ping_status' => null, 'comment_status' => null, 'custom_fields' => null, 'terms_names' => null, 'terms' => null, 'sticky' => null, 'enclosure' => null, 'ID' => null, ); $post_data = wp_parse_args( array_intersect_key( $content_struct, $defaults ), $defaults ); $post_type = get_post_type_object( $post_data['post_type'] ); if ( ! $post_type ) { return new IXR_Error( 403, __( 'Invalid post type.' ) ); } $update = ! empty( $post_data['ID'] ); if ( $update ) { if ( ! get_post( $post_data['ID'] ) ) { return new IXR_Error( 401, __( 'Invalid post ID.' ) ); } if ( ! current_user_can( 'edit_post', $post_data['ID'] ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) ); } if ( get_post_type( $post_data['ID'] ) !== $post_data['post_type'] ) { return new IXR_Error( 401, __( 'The post type may not be changed.' ) ); } } else { if ( ! current_user_can( $post_type->cap->create_posts ) || ! current_user_can( $post_type->cap->edit_posts ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to post on this site.' ) ); } } switch ( $post_data['post_status'] ) { case 'draft': case 'pending': break; case 'private': if ( ! current_user_can( $post_type->cap->publish_posts ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to create private posts in this post type.' ) ); } break; case 'publish': case 'future': if ( ! current_user_can( $post_type->cap->publish_posts ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to publish posts in this post type.' ) ); } break; default: if ( ! get_post_status_object( $post_data['post_status'] ) ) { $post_data['post_status'] = 'draft'; } break; } if ( ! empty( $post_data['post_password'] ) && ! current_user_can( $post_type->cap->publish_posts ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to create password protected posts in this post type.' ) ); } $post_data['post_author'] = absint( $post_data['post_author'] ); if ( ! empty( $post_data['post_author'] ) && $post_data['post_author'] != $user->ID ) { if ( ! current_user_can( $post_type->cap->edit_others_posts ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to create posts as this user.' ) ); } $author = get_userdata( $post_data['post_author'] ); if ( ! $author ) { return new IXR_Error( 404, __( 'Invalid author ID.' ) ); } } else { $post_data['post_author'] = $user->ID; } if ( isset( $post_data['comment_status'] ) && 'open' !== $post_data['comment_status'] && 'closed' !== $post_data['comment_status'] ) { unset( $post_data['comment_status'] ); } if ( isset( $post_data['ping_status'] ) && 'open' !== $post_data['ping_status'] && 'closed' !== $post_data['ping_status'] ) { unset( $post_data['ping_status'] ); } // Do some timestamp voodoo. if ( ! empty( $post_data['post_date_gmt'] ) ) { // We know this is supposed to be GMT, so we're going to slap that Z on there by force. $dateCreated = rtrim( $post_data['post_date_gmt']->getIso(), 'Z' ) . 'Z'; } elseif ( ! empty( $post_data['post_date'] ) ) { $dateCreated = $post_data['post_date']->getIso(); } // Default to not flagging the post date to be edited unless it's intentional. $post_data['edit_date'] = false; if ( ! empty( $dateCreated ) ) { $post_data['post_date'] = iso8601_to_datetime( $dateCreated ); $post_data['post_date_gmt'] = iso8601_to_datetime( $dateCreated, 'gmt' ); // Flag the post date to be edited. $post_data['edit_date'] = true; } if ( ! isset( $post_data['ID'] ) ) { $post_data['ID'] = get_default_post_to_edit( $post_data['post_type'], true )->ID; } $post_ID = $post_data['ID']; if ( 'post' === $post_data['post_type'] ) { $error = $this->_toggle_sticky( $post_data, $update ); if ( $error ) { return $error; } } if ( isset( $post_data['post_thumbnail'] ) ) { // Empty value deletes, non-empty value adds/updates. if ( ! $post_data['post_thumbnail'] ) { delete_post_thumbnail( $post_ID ); } elseif ( ! get_post( absint( $post_data['post_thumbnail'] ) ) ) { return new IXR_Error( 404, __( 'Invalid attachment ID.' ) ); } set_post_thumbnail( $post_ID, $post_data['post_thumbnail'] ); unset( $content_struct['post_thumbnail'] ); } if ( isset( $post_data['custom_fields'] ) ) { $this->set_custom_fields( $post_ID, $post_data['custom_fields'] ); } if ( isset( $post_data['terms'] ) || isset( $post_data['terms_names'] ) ) { $post_type_taxonomies = get_object_taxonomies( $post_data['post_type'], 'objects' ); // Accumulate term IDs from terms and terms_names. $terms = array(); // First validate the terms specified by ID. if ( isset( $post_data['terms'] ) && is_array( $post_data['terms'] ) ) { $taxonomies = array_keys( $post_data['terms'] ); // Validating term IDs. foreach ( $taxonomies as $taxonomy ) { if ( ! array_key_exists( $taxonomy, $post_type_taxonomies ) ) { return new IXR_Error( 401, __( 'Sorry, one of the given taxonomies is not supported by the post type.' ) ); } if ( ! current_user_can( $post_type_taxonomies[ $taxonomy ]->cap->assign_terms ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to assign a term to one of the given taxonomies.' ) ); } $term_ids = $post_data['terms'][ $taxonomy ]; $terms[ $taxonomy ] = array(); foreach ( $term_ids as $term_id ) { $term = get_term_by( 'id', $term_id, $taxonomy ); if ( ! $term ) { return new IXR_Error( 403, __( 'Invalid term ID.' ) ); } $terms[ $taxonomy ][] = (int) $term_id; } } } // Now validate terms specified by name. if ( isset( $post_data['terms_names'] ) && is_array( $post_data['terms_names'] ) ) { $taxonomies = array_keys( $post_data['terms_names'] ); foreach ( $taxonomies as $taxonomy ) { if ( ! array_key_exists( $taxonomy, $post_type_taxonomies ) ) { return new IXR_Error( 401, __( 'Sorry, one of the given taxonomies is not supported by the post type.' ) ); } if ( ! current_user_can( $post_type_taxonomies[ $taxonomy ]->cap->assign_terms ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to assign a term to one of the given taxonomies.' ) ); } /* * For hierarchical taxonomies, we can't assign a term when multiple terms * in the hierarchy share the same name. */ $ambiguous_terms = array(); if ( is_taxonomy_hierarchical( $taxonomy ) ) { $tax_term_names = get_terms( array( 'taxonomy' => $taxonomy, 'fields' => 'names', 'hide_empty' => false, ) ); // Count the number of terms with the same name. $tax_term_names_count = array_count_values( $tax_term_names ); // Filter out non-ambiguous term names. $ambiguous_tax_term_counts = array_filter( $tax_term_names_count, array( $this, '_is_greater_than_one' ) ); $ambiguous_terms = array_keys( $ambiguous_tax_term_counts ); } $term_names = $post_data['terms_names'][ $taxonomy ]; foreach ( $term_names as $term_name ) { if ( in_array( $term_name, $ambiguous_terms, true ) ) { return new IXR_Error( 401, __( 'Ambiguous term name used in a hierarchical taxonomy. Please use term ID instead.' ) ); } $term = get_term_by( 'name', $term_name, $taxonomy ); if ( ! $term ) { // Term doesn't exist, so check that the user is allowed to create new terms. if ( ! current_user_can( $post_type_taxonomies[ $taxonomy ]->cap->edit_terms ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to add a term to one of the given taxonomies.' ) ); } // Create the new term. $term_info = wp_insert_term( $term_name, $taxonomy ); if ( is_wp_error( $term_info ) ) { return new IXR_Error( 500, $term_info->get_error_message() ); } $terms[ $taxonomy ][] = (int) $term_info['term_id']; } else { $terms[ $taxonomy ][] = (int) $term->term_id; } } } } $post_data['tax_input'] = $terms; unset( $post_data['terms'], $post_data['terms_names'] ); } if ( isset( $post_data['post_format'] ) ) { $format = set_post_format( $post_ID, $post_data['post_format'] ); if ( is_wp_error( $format ) ) { return new IXR_Error( 500, $format->get_error_message() ); } unset( $post_data['post_format'] ); } // Handle enclosures. $enclosure = isset( $post_data['enclosure'] ) ? $post_data['enclosure'] : null; $this->add_enclosure_if_new( $post_ID, $enclosure ); $this->attach_uploads( $post_ID, $post_data['post_content'] ); /** * Filters post data array to be inserted via XML-RPC. * * @since 3.4.0 * * @param array $post_data Parsed array of post data. * @param array $content_struct Post data array. */ $post_data = apply_filters( 'xmlrpc_wp_insert_post_data', $post_data, $content_struct ); $post_ID = $update ? wp_update_post( $post_data, true ) : wp_insert_post( $post_data, true ); if ( is_wp_error( $post_ID ) ) { return new IXR_Error( 500, $post_ID->get_error_message() ); } if ( ! $post_ID ) { if ( $update ) { return new IXR_Error( 401, __( 'Sorry, the post could not be updated.' ) ); } else { return new IXR_Error( 401, __( 'Sorry, the post could not be created.' ) ); } } return (string) $post_ID; } /** * Edit a post for any registered post type. * * The $content_struct parameter only needs to contain fields that * should be changed. All other fields will retain their existing values. * * @since 3.4.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id Blog ID (unused). * @type string $username Username. * @type string $password Password. * @type int $post_id Post ID. * @type array $content_struct Extra content arguments. * } * @return true|IXR_Error True on success, IXR_Error on failure. */ public function wp_editPost( $args ) { if ( ! $this->minimum_args( $args, 5 ) ) { return $this->error; } $this->escape( $args ); $username = $args[1]; $password = $args[2]; $post_id = (int) $args[3]; $content_struct = $args[4]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.editPost', $args, $this ); $post = get_post( $post_id, ARRAY_A ); if ( empty( $post['ID'] ) ) { return new IXR_Error( 404, __( 'Invalid post ID.' ) ); } if ( isset( $content_struct['if_not_modified_since'] ) ) { // If the post has been modified since the date provided, return an error. if ( mysql2date( 'U', $post['post_modified_gmt'] ) > $content_struct['if_not_modified_since']->getTimestamp() ) { return new IXR_Error( 409, __( 'There is a revision of this post that is more recent.' ) ); } } // Convert the date field back to IXR form. $post['post_date'] = $this->_convert_date( $post['post_date'] ); /* * Ignore the existing GMT date if it is empty or a non-GMT date was supplied in $content_struct, * since _insert_post() will ignore the non-GMT date if the GMT date is set. */ if ( '0000-00-00 00:00:00' === $post['post_date_gmt'] || isset( $content_struct['post_date'] ) ) { unset( $post['post_date_gmt'] ); } else { $post['post_date_gmt'] = $this->_convert_date( $post['post_date_gmt'] ); } /* * If the API client did not provide 'post_date', then we must not perpetuate the value that * was stored in the database, or it will appear to be an intentional edit. Conveying it here * as if it was coming from the API client will cause an otherwise zeroed out 'post_date_gmt' * to get set with the value that was originally stored in the database when the draft was created. */ if ( ! isset( $content_struct['post_date'] ) ) { unset( $post['post_date'] ); } $this->escape( $post ); $merged_content_struct = array_merge( $post, $content_struct ); $retval = $this->_insert_post( $user, $merged_content_struct ); if ( $retval instanceof IXR_Error ) { return $retval; } return true; } /** * Delete a post for any registered post type. * * @since 3.4.0 * * @see wp_delete_post() * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id Blog ID (unused). * @type string $username Username. * @type string $password Password. * @type int $post_id Post ID. * } * @return true|IXR_Error True on success, IXR_Error instance on failure. */ public function wp_deletePost( $args ) { if ( ! $this->minimum_args( $args, 4 ) ) { return $this->error; } $this->escape( $args ); $username = $args[1]; $password = $args[2]; $post_id = (int) $args[3]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.deletePost', $args, $this ); $post = get_post( $post_id, ARRAY_A ); if ( empty( $post['ID'] ) ) { return new IXR_Error( 404, __( 'Invalid post ID.' ) ); } if ( ! current_user_can( 'delete_post', $post_id ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to delete this post.' ) ); } $result = wp_delete_post( $post_id ); if ( ! $result ) { return new IXR_Error( 500, __( 'Sorry, the post could not be deleted.' ) ); } return true; } /** * Retrieve a post. * * @since 3.4.0 * * The optional $fields parameter specifies what fields will be included * in the response array. This should be a list of field names. 'post_id' will * always be included in the response regardless of the value of $fields. * * Instead of, or in addition to, individual field names, conceptual group * names can be used to specify multiple fields. The available conceptual * groups are 'post' (all basic fields), 'taxonomies', 'custom_fields', * and 'enclosure'. * * @see get_post() * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id Blog ID (unused). * @type string $username Username. * @type string $password Password. * @type int $post_id Post ID. * @type array $fields The subset of post type fields to return. * } * @return array|IXR_Error Array contains (based on $fields parameter): * - 'post_id' * - 'post_title' * - 'post_date' * - 'post_date_gmt' * - 'post_modified' * - 'post_modified_gmt' * - 'post_status' * - 'post_type' * - 'post_name' * - 'post_author' * - 'post_password' * - 'post_excerpt' * - 'post_content' * - 'link' * - 'comment_status' * - 'ping_status' * - 'sticky' * - 'custom_fields' * - 'terms' * - 'categories' * - 'tags' * - 'enclosure' */ public function wp_getPost( $args ) { if ( ! $this->minimum_args( $args, 4 ) ) { return $this->error; } $this->escape( $args ); $username = $args[1]; $password = $args[2]; $post_id = (int) $args[3]; if ( isset( $args[4] ) ) { $fields = $args[4]; } else { /** * Filters the list of post query fields used by the given XML-RPC method. * * @since 3.4.0 * * @param array $fields Array of post fields. Default array contains 'post', 'terms', and 'custom_fields'. * @param string $method Method name. */ $fields = apply_filters( 'xmlrpc_default_post_fields', array( 'post', 'terms', 'custom_fields' ), 'wp.getPost' ); } $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getPost', $args, $this ); $post = get_post( $post_id, ARRAY_A ); if ( empty( $post['ID'] ) ) { return new IXR_Error( 404, __( 'Invalid post ID.' ) ); } if ( ! current_user_can( 'edit_post', $post_id ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) ); } return $this->_prepare_post( $post, $fields ); } /** * Retrieve posts. * * @since 3.4.0 * * @see wp_get_recent_posts() * @see wp_getPost() for more on `$fields` * @see get_posts() for more on `$filter` values * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id Blog ID (unused). * @type string $username Username. * @type string $password Password. * @type array $filter Optional. Modifies the query used to retrieve posts. Accepts 'post_type', * 'post_status', 'number', 'offset', 'orderby', 's', and 'order'. * Default empty array. * @type array $fields Optional. The subset of post type fields to return in the response array. * } * @return array|IXR_Error Array contains a collection of posts. */ public function wp_getPosts( $args ) { if ( ! $this->minimum_args( $args, 3 ) ) { return $this->error; } $this->escape( $args ); $username = $args[1]; $password = $args[2]; $filter = isset( $args[3] ) ? $args[3] : array(); if ( isset( $args[4] ) ) { $fields = $args[4]; } else { /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ $fields = apply_filters( 'xmlrpc_default_post_fields', array( 'post', 'terms', 'custom_fields' ), 'wp.getPosts' ); } $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getPosts', $args, $this ); $query = array(); if ( isset( $filter['post_type'] ) ) { $post_type = get_post_type_object( $filter['post_type'] ); if ( ! ( (bool) $post_type ) ) { return new IXR_Error( 403, __( 'Invalid post type.' ) ); } } else { $post_type = get_post_type_object( 'post' ); } if ( ! current_user_can( $post_type->cap->edit_posts ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit posts in this post type.' ) ); } $query['post_type'] = $post_type->name; if ( isset( $filter['post_status'] ) ) { $query['post_status'] = $filter['post_status']; } if ( isset( $filter['number'] ) ) { $query['numberposts'] = absint( $filter['number'] ); } if ( isset( $filter['offset'] ) ) { $query['offset'] = absint( $filter['offset'] ); } if ( isset( $filter['orderby'] ) ) { $query['orderby'] = $filter['orderby']; if ( isset( $filter['order'] ) ) { $query['order'] = $filter['order']; } } if ( isset( $filter['s'] ) ) { $query['s'] = $filter['s']; } $posts_list = wp_get_recent_posts( $query ); if ( ! $posts_list ) { return array(); } // Holds all the posts data. $struct = array(); foreach ( $posts_list as $post ) { if ( ! current_user_can( 'edit_post', $post['ID'] ) ) { continue; } $struct[] = $this->_prepare_post( $post, $fields ); } return $struct; } /** * Create a new term. * * @since 3.4.0 * * @see wp_insert_term() * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id Blog ID (unused). * @type string $username Username. * @type string $password Password. * @type array $content_struct Content struct for adding a new term. The struct must contain * the term 'name' and 'taxonomy'. Optional accepted values include * 'parent', 'description', and 'slug'. * } * @return int|IXR_Error The term ID on success, or an IXR_Error object on failure. */ public function wp_newTerm( $args ) { if ( ! $this->minimum_args( $args, 4 ) ) { return $this->error; } $this->escape( $args ); $username = $args[1]; $password = $args[2]; $content_struct = $args[3]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.newTerm', $args, $this ); if ( ! taxonomy_exists( $content_struct['taxonomy'] ) ) { return new IXR_Error( 403, __( 'Invalid taxonomy.' ) ); } $taxonomy = get_taxonomy( $content_struct['taxonomy'] ); if ( ! current_user_can( $taxonomy->cap->edit_terms ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to create terms in this taxonomy.' ) ); } $taxonomy = (array) $taxonomy; // Hold the data of the term. $term_data = array(); $term_data['name'] = trim( $content_struct['name'] ); if ( empty( $term_data['name'] ) ) { return new IXR_Error( 403, __( 'The term name cannot be empty.' ) ); } if ( isset( $content_struct['parent'] ) ) { if ( ! $taxonomy['hierarchical'] ) { return new IXR_Error( 403, __( 'This taxonomy is not hierarchical.' ) ); } $parent_term_id = (int) $content_struct['parent']; $parent_term = get_term( $parent_term_id, $taxonomy['name'] ); if ( is_wp_error( $parent_term ) ) { return new IXR_Error( 500, $parent_term->get_error_message() ); } if ( ! $parent_term ) { return new IXR_Error( 403, __( 'Parent term does not exist.' ) ); } $term_data['parent'] = $content_struct['parent']; } if ( isset( $content_struct['description'] ) ) { $term_data['description'] = $content_struct['description']; } if ( isset( $content_struct['slug'] ) ) { $term_data['slug'] = $content_struct['slug']; } $term = wp_insert_term( $term_data['name'], $taxonomy['name'], $term_data ); if ( is_wp_error( $term ) ) { return new IXR_Error( 500, $term->get_error_message() ); } if ( ! $term ) { return new IXR_Error( 500, __( 'Sorry, the term could not be created.' ) ); } // Add term meta. if ( isset( $content_struct['custom_fields'] ) ) { $this->set_term_custom_fields( $term['term_id'], $content_struct['custom_fields'] ); } return (string) $term['term_id']; } /** * Edit a term. * * @since 3.4.0 * * @see wp_update_term() * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id Blog ID (unused). * @type string $username Username. * @type string $password Password. * @type int $term_id Term ID. * @type array $content_struct Content struct for editing a term. The struct must contain the * term ''taxonomy'. Optional accepted values include 'name', 'parent', * 'description', and 'slug'. * } * @return true|IXR_Error True on success, IXR_Error instance on failure. */ public function wp_editTerm( $args ) { if ( ! $this->minimum_args( $args, 5 ) ) { return $this->error; } $this->escape( $args ); $username = $args[1]; $password = $args[2]; $term_id = (int) $args[3]; $content_struct = $args[4]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.editTerm', $args, $this ); if ( ! taxonomy_exists( $content_struct['taxonomy'] ) ) { return new IXR_Error( 403, __( 'Invalid taxonomy.' ) ); } $taxonomy = get_taxonomy( $content_struct['taxonomy'] ); $taxonomy = (array) $taxonomy; // Hold the data of the term. $term_data = array(); $term = get_term( $term_id, $content_struct['taxonomy'] ); if ( is_wp_error( $term ) ) { return new IXR_Error( 500, $term->get_error_message() ); } if ( ! $term ) { return new IXR_Error( 404, __( 'Invalid term ID.' ) ); } if ( ! current_user_can( 'edit_term', $term_id ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this term.' ) ); } if ( isset( $content_struct['name'] ) ) { $term_data['name'] = trim( $content_struct['name'] ); if ( empty( $term_data['name'] ) ) { return new IXR_Error( 403, __( 'The term name cannot be empty.' ) ); } } if ( ! empty( $content_struct['parent'] ) ) { if ( ! $taxonomy['hierarchical'] ) { return new IXR_Error( 403, __( 'Cannot set parent term, taxonomy is not hierarchical.' ) ); } $parent_term_id = (int) $content_struct['parent']; $parent_term = get_term( $parent_term_id, $taxonomy['name'] ); if ( is_wp_error( $parent_term ) ) { return new IXR_Error( 500, $parent_term->get_error_message() ); } if ( ! $parent_term ) { return new IXR_Error( 403, __( 'Parent term does not exist.' ) ); } $term_data['parent'] = $content_struct['parent']; } if ( isset( $content_struct['description'] ) ) { $term_data['description'] = $content_struct['description']; } if ( isset( $content_struct['slug'] ) ) { $term_data['slug'] = $content_struct['slug']; } $term = wp_update_term( $term_id, $taxonomy['name'], $term_data ); if ( is_wp_error( $term ) ) { return new IXR_Error( 500, $term->get_error_message() ); } if ( ! $term ) { return new IXR_Error( 500, __( 'Sorry, editing the term failed.' ) ); } // Update term meta. if ( isset( $content_struct['custom_fields'] ) ) { $this->set_term_custom_fields( $term_id, $content_struct['custom_fields'] ); } return true; } /** * Delete a term. * * @since 3.4.0 * * @see wp_delete_term() * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id Blog ID (unused). * @type string $username Username. * @type string $password Password. * @type string $taxnomy_name Taxonomy name. * @type int $term_id Term ID. * } * @return true|IXR_Error True on success, IXR_Error instance on failure. */ public function wp_deleteTerm( $args ) { if ( ! $this->minimum_args( $args, 5 ) ) { return $this->error; } $this->escape( $args ); $username = $args[1]; $password = $args[2]; $taxonomy = $args[3]; $term_id = (int) $args[4]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.deleteTerm', $args, $this ); if ( ! taxonomy_exists( $taxonomy ) ) { return new IXR_Error( 403, __( 'Invalid taxonomy.' ) ); } $taxonomy = get_taxonomy( $taxonomy ); $term = get_term( $term_id, $taxonomy->name ); if ( is_wp_error( $term ) ) { return new IXR_Error( 500, $term->get_error_message() ); } if ( ! $term ) { return new IXR_Error( 404, __( 'Invalid term ID.' ) ); } if ( ! current_user_can( 'delete_term', $term_id ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to delete this term.' ) ); } $result = wp_delete_term( $term_id, $taxonomy->name ); if ( is_wp_error( $result ) ) { return new IXR_Error( 500, $term->get_error_message() ); } if ( ! $result ) { return new IXR_Error( 500, __( 'Sorry, deleting the term failed.' ) ); } return $result; } /** * Retrieve a term. * * @since 3.4.0 * * @see get_term() * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id Blog ID (unused). * @type string $username Username. * @type string $password Password. * @type string $taxnomy Taxonomy name. * @type string $term_id Term ID. * } * @return array|IXR_Error IXR_Error on failure, array on success, containing: * - 'term_id' * - 'name' * - 'slug' * - 'term_group' * - 'term_taxonomy_id' * - 'taxonomy' * - 'description' * - 'parent' * - 'count' */ public function wp_getTerm( $args ) { if ( ! $this->minimum_args( $args, 5 ) ) { return $this->error; } $this->escape( $args ); $username = $args[1]; $password = $args[2]; $taxonomy = $args[3]; $term_id = (int) $args[4]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getTerm', $args, $this ); if ( ! taxonomy_exists( $taxonomy ) ) { return new IXR_Error( 403, __( 'Invalid taxonomy.' ) ); } $taxonomy = get_taxonomy( $taxonomy ); $term = get_term( $term_id, $taxonomy->name, ARRAY_A ); if ( is_wp_error( $term ) ) { return new IXR_Error( 500, $term->get_error_message() ); } if ( ! $term ) { return new IXR_Error( 404, __( 'Invalid term ID.' ) ); } if ( ! current_user_can( 'assign_term', $term_id ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to assign this term.' ) ); } return $this->_prepare_term( $term ); } /** * Retrieve all terms for a taxonomy. * * @since 3.4.0 * * The optional $filter parameter modifies the query used to retrieve terms. * Accepted keys are 'number', 'offset', 'orderby', 'order', 'hide_empty', and 'search'. * * @see get_terms() * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id Blog ID (unused). * @type string $username Username. * @type string $password Password. * @type string $taxnomy Taxonomy name. * @type array $filter Optional. Modifies the query used to retrieve posts. Accepts 'number', * 'offset', 'orderby', 'order', 'hide_empty', and 'search'. Default empty array. * } * @return array|IXR_Error An associative array of terms data on success, IXR_Error instance otherwise. */ public function wp_getTerms( $args ) { if ( ! $this->minimum_args( $args, 4 ) ) { return $this->error; } $this->escape( $args ); $username = $args[1]; $password = $args[2]; $taxonomy = $args[3]; $filter = isset( $args[4] ) ? $args[4] : array(); $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getTerms', $args, $this ); if ( ! taxonomy_exists( $taxonomy ) ) { return new IXR_Error( 403, __( 'Invalid taxonomy.' ) ); } $taxonomy = get_taxonomy( $taxonomy ); if ( ! current_user_can( $taxonomy->cap->assign_terms ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to assign terms in this taxonomy.' ) ); } $query = array( 'taxonomy' => $taxonomy->name ); if ( isset( $filter['number'] ) ) { $query['number'] = absint( $filter['number'] ); } if ( isset( $filter['offset'] ) ) { $query['offset'] = absint( $filter['offset'] ); } if ( isset( $filter['orderby'] ) ) { $query['orderby'] = $filter['orderby']; if ( isset( $filter['order'] ) ) { $query['order'] = $filter['order']; } } if ( isset( $filter['hide_empty'] ) ) { $query['hide_empty'] = $filter['hide_empty']; } else { $query['get'] = 'all'; } if ( isset( $filter['search'] ) ) { $query['search'] = $filter['search']; } $terms = get_terms( $query ); if ( is_wp_error( $terms ) ) { return new IXR_Error( 500, $terms->get_error_message() ); } $struct = array(); foreach ( $terms as $term ) { $struct[] = $this->_prepare_term( $term ); } return $struct; } /** * Retrieve a taxonomy. * * @since 3.4.0 * * @see get_taxonomy() * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id Blog ID (unused). * @type string $username Username. * @type string $password Password. * @type string $taxnomy Taxonomy name. * @type array $fields Optional. Array of taxonomy fields to limit to in the return. * Accepts 'labels', 'cap', 'menu', and 'object_type'. * Default empty array. * } * @return array|IXR_Error An array of taxonomy data on success, IXR_Error instance otherwise. */ public function wp_getTaxonomy( $args ) { if ( ! $this->minimum_args( $args, 4 ) ) { return $this->error; } $this->escape( $args ); $username = $args[1]; $password = $args[2]; $taxonomy = $args[3]; if ( isset( $args[4] ) ) { $fields = $args[4]; } else { /** * Filters the taxonomy query fields used by the given XML-RPC method. * * @since 3.4.0 * * @param array $fields An array of taxonomy fields to retrieve. * @param string $method The method name. */ $fields = apply_filters( 'xmlrpc_default_taxonomy_fields', array( 'labels', 'cap', 'object_type' ), 'wp.getTaxonomy' ); } $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getTaxonomy', $args, $this ); if ( ! taxonomy_exists( $taxonomy ) ) { return new IXR_Error( 403, __( 'Invalid taxonomy.' ) ); } $taxonomy = get_taxonomy( $taxonomy ); if ( ! current_user_can( $taxonomy->cap->assign_terms ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to assign terms in this taxonomy.' ) ); } return $this->_prepare_taxonomy( $taxonomy, $fields ); } /** * Retrieve all taxonomies. * * @since 3.4.0 * * @see get_taxonomies() * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id Blog ID (unused). * @type string $username Username. * @type string $password Password. * @type array $filter Optional. An array of arguments for retrieving taxonomies. * @type array $fields Optional. The subset of taxonomy fields to return. * } * @return array|IXR_Error An associative array of taxonomy data with returned fields determined * by `$fields`, or an IXR_Error instance on failure. */ public function wp_getTaxonomies( $args ) { if ( ! $this->minimum_args( $args, 3 ) ) { return $this->error; } $this->escape( $args ); $username = $args[1]; $password = $args[2]; $filter = isset( $args[3] ) ? $args[3] : array( 'public' => true ); if ( isset( $args[4] ) ) { $fields = $args[4]; } else { /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ $fields = apply_filters( 'xmlrpc_default_taxonomy_fields', array( 'labels', 'cap', 'object_type' ), 'wp.getTaxonomies' ); } $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getTaxonomies', $args, $this ); $taxonomies = get_taxonomies( $filter, 'objects' ); // Holds all the taxonomy data. $struct = array(); foreach ( $taxonomies as $taxonomy ) { // Capability check for post types. if ( ! current_user_can( $taxonomy->cap->assign_terms ) ) { continue; } $struct[] = $this->_prepare_taxonomy( $taxonomy, $fields ); } return $struct; } /** * Retrieve a user. * * The optional $fields parameter specifies what fields will be included * in the response array. This should be a list of field names. 'user_id' will * always be included in the response regardless of the value of $fields. * * Instead of, or in addition to, individual field names, conceptual group * names can be used to specify multiple fields. The available conceptual * groups are 'basic' and 'all'. * * @uses get_userdata() * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type string $username * @type string $password * @type int $user_id * @type array $fields (optional) * } * @return array|IXR_Error Array contains (based on $fields parameter): * - 'user_id' * - 'username' * - 'first_name' * - 'last_name' * - 'registered' * - 'bio' * - 'email' * - 'nickname' * - 'nicename' * - 'url' * - 'display_name' * - 'roles' */ public function wp_getUser( $args ) { if ( ! $this->minimum_args( $args, 4 ) ) { return $this->error; } $this->escape( $args ); $username = $args[1]; $password = $args[2]; $user_id = (int) $args[3]; if ( isset( $args[4] ) ) { $fields = $args[4]; } else { /** * Filters the default user query fields used by the given XML-RPC method. * * @since 3.5.0 * * @param array $fields User query fields for given method. Default 'all'. * @param string $method The method name. */ $fields = apply_filters( 'xmlrpc_default_user_fields', array( 'all' ), 'wp.getUser' ); } $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getUser', $args, $this ); if ( ! current_user_can( 'edit_user', $user_id ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this user.' ) ); } $user_data = get_userdata( $user_id ); if ( ! $user_data ) { return new IXR_Error( 404, __( 'Invalid user ID.' ) ); } return $this->_prepare_user( $user_data, $fields ); } /** * Retrieve users. * * The optional $filter parameter modifies the query used to retrieve users. * Accepted keys are 'number' (default: 50), 'offset' (default: 0), 'role', * 'who', 'orderby', and 'order'. * * The optional $fields parameter specifies what fields will be included * in the response array. * * @uses get_users() * @see wp_getUser() for more on $fields and return values * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type string $username * @type string $password * @type array $filter (optional) * @type array $fields (optional) * } * @return array|IXR_Error users data */ public function wp_getUsers( $args ) { if ( ! $this->minimum_args( $args, 3 ) ) { return $this->error; } $this->escape( $args ); $username = $args[1]; $password = $args[2]; $filter = isset( $args[3] ) ? $args[3] : array(); if ( isset( $args[4] ) ) { $fields = $args[4]; } else { /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ $fields = apply_filters( 'xmlrpc_default_user_fields', array( 'all' ), 'wp.getUsers' ); } $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getUsers', $args, $this ); if ( ! current_user_can( 'list_users' ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to list users.' ) ); } $query = array( 'fields' => 'all_with_meta' ); $query['number'] = ( isset( $filter['number'] ) ) ? absint( $filter['number'] ) : 50; $query['offset'] = ( isset( $filter['offset'] ) ) ? absint( $filter['offset'] ) : 0; if ( isset( $filter['orderby'] ) ) { $query['orderby'] = $filter['orderby']; if ( isset( $filter['order'] ) ) { $query['order'] = $filter['order']; } } if ( isset( $filter['role'] ) ) { if ( get_role( $filter['role'] ) === null ) { return new IXR_Error( 403, __( 'Invalid role.' ) ); } $query['role'] = $filter['role']; } if ( isset( $filter['who'] ) ) { $query['who'] = $filter['who']; } $users = get_users( $query ); $_users = array(); foreach ( $users as $user_data ) { if ( current_user_can( 'edit_user', $user_data->ID ) ) { $_users[] = $this->_prepare_user( $user_data, $fields ); } } return $_users; } /** * Retrieve information about the requesting user. * * @uses get_userdata() * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type string $username * @type string $password * @type array $fields (optional) * } * @return array|IXR_Error (@see wp_getUser) */ public function wp_getProfile( $args ) { if ( ! $this->minimum_args( $args, 3 ) ) { return $this->error; } $this->escape( $args ); $username = $args[1]; $password = $args[2]; if ( isset( $args[3] ) ) { $fields = $args[3]; } else { /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ $fields = apply_filters( 'xmlrpc_default_user_fields', array( 'all' ), 'wp.getProfile' ); } $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getProfile', $args, $this ); if ( ! current_user_can( 'edit_user', $user->ID ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit your profile.' ) ); } $user_data = get_userdata( $user->ID ); return $this->_prepare_user( $user_data, $fields ); } /** * Edit user's profile. * * @uses wp_update_user() * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type string $username * @type string $password * @type array $content_struct It can optionally contain: * - 'first_name' * - 'last_name' * - 'website' * - 'display_name' * - 'nickname' * - 'nicename' * - 'bio' * } * @return true|IXR_Error True, on success. */ public function wp_editProfile( $args ) { if ( ! $this->minimum_args( $args, 4 ) ) { return $this->error; } $this->escape( $args ); $username = $args[1]; $password = $args[2]; $content_struct = $args[3]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.editProfile', $args, $this ); if ( ! current_user_can( 'edit_user', $user->ID ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit your profile.' ) ); } // Holds data of the user. $user_data = array(); $user_data['ID'] = $user->ID; // Only set the user details if they were given. if ( isset( $content_struct['first_name'] ) ) { $user_data['first_name'] = $content_struct['first_name']; } if ( isset( $content_struct['last_name'] ) ) { $user_data['last_name'] = $content_struct['last_name']; } if ( isset( $content_struct['url'] ) ) { $user_data['user_url'] = $content_struct['url']; } if ( isset( $content_struct['display_name'] ) ) { $user_data['display_name'] = $content_struct['display_name']; } if ( isset( $content_struct['nickname'] ) ) { $user_data['nickname'] = $content_struct['nickname']; } if ( isset( $content_struct['nicename'] ) ) { $user_data['user_nicename'] = $content_struct['nicename']; } if ( isset( $content_struct['bio'] ) ) { $user_data['description'] = $content_struct['bio']; } $result = wp_update_user( $user_data ); if ( is_wp_error( $result ) ) { return new IXR_Error( 500, $result->get_error_message() ); } if ( ! $result ) { return new IXR_Error( 500, __( 'Sorry, the user could not be updated.' ) ); } return true; } /** * Retrieve page. * * @since 2.2.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type int $page_id * @type string $username * @type string $password * } * @return array|IXR_Error */ public function wp_getPage( $args ) { $this->escape( $args ); $page_id = (int) $args[1]; $username = $args[2]; $password = $args[3]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } $page = get_post( $page_id ); if ( ! $page ) { return new IXR_Error( 404, __( 'Invalid post ID.' ) ); } if ( ! current_user_can( 'edit_page', $page_id ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this page.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getPage', $args, $this ); // If we found the page then format the data. if ( $page->ID && ( 'page' === $page->post_type ) ) { return $this->_prepare_page( $page ); } else { // If the page doesn't exist, indicate that. return new IXR_Error( 404, __( 'Sorry, no such page.' ) ); } } /** * Retrieve Pages. * * @since 2.2.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type string $username * @type string $password * @type int $num_pages * } * @return array|IXR_Error */ public function wp_getPages( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $num_pages = isset( $args[3] ) ? (int) $args[3] : 10; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! current_user_can( 'edit_pages' ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit pages.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getPages', $args, $this ); $pages = get_posts( array( 'post_type' => 'page', 'post_status' => 'any', 'numberposts' => $num_pages, ) ); $num_pages = count( $pages ); // If we have pages, put together their info. if ( $num_pages >= 1 ) { $pages_struct = array(); foreach ( $pages as $page ) { if ( current_user_can( 'edit_page', $page->ID ) ) { $pages_struct[] = $this->_prepare_page( $page ); } } return $pages_struct; } return array(); } /** * Create new page. * * @since 2.2.0 * * @see wp_xmlrpc_server::mw_newPost() * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type string $username * @type string $password * @type array $content_struct * } * @return int|IXR_Error */ public function wp_newPage( $args ) { // Items not escaped here will be escaped in wp_newPost(). $username = $this->escape( $args[1] ); $password = $this->escape( $args[2] ); $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.newPage', $args, $this ); // Mark this as content for a page. $args[3]['post_type'] = 'page'; // Let mw_newPost() do all of the heavy lifting. return $this->mw_newPost( $args ); } /** * Delete page. * * @since 2.2.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type string $username * @type string $password * @type int $page_id * } * @return true|IXR_Error True, if success. */ public function wp_deletePage( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $page_id = (int) $args[3]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.deletePage', $args, $this ); // Get the current page based on the 'page_id' and // make sure it is a page and not a post. $actual_page = get_post( $page_id, ARRAY_A ); if ( ! $actual_page || ( 'page' !== $actual_page['post_type'] ) ) { return new IXR_Error( 404, __( 'Sorry, no such page.' ) ); } // Make sure the user can delete pages. if ( ! current_user_can( 'delete_page', $page_id ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to delete this page.' ) ); } // Attempt to delete the page. $result = wp_delete_post( $page_id ); if ( ! $result ) { return new IXR_Error( 500, __( 'Failed to delete the page.' ) ); } /** * Fires after a page has been successfully deleted via XML-RPC. * * @since 3.4.0 * * @param int $page_id ID of the deleted page. * @param array $args An array of arguments to delete the page. */ do_action( 'xmlrpc_call_success_wp_deletePage', $page_id, $args ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase return true; } /** * Edit page. * * @since 2.2.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type int $page_id * @type string $username * @type string $password * @type string $content * @type string $publish * } * @return array|IXR_Error */ public function wp_editPage( $args ) { // Items will be escaped in mw_editPost(). $page_id = (int) $args[1]; $username = $args[2]; $password = $args[3]; $content = $args[4]; $publish = $args[5]; $escaped_username = $this->escape( $username ); $escaped_password = $this->escape( $password ); $user = $this->login( $escaped_username, $escaped_password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.editPage', $args, $this ); // Get the page data and make sure it is a page. $actual_page = get_post( $page_id, ARRAY_A ); if ( ! $actual_page || ( 'page' !== $actual_page['post_type'] ) ) { return new IXR_Error( 404, __( 'Sorry, no such page.' ) ); } // Make sure the user is allowed to edit pages. if ( ! current_user_can( 'edit_page', $page_id ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this page.' ) ); } // Mark this as content for a page. $content['post_type'] = 'page'; // Arrange args in the way mw_editPost() understands. $args = array( $page_id, $username, $password, $content, $publish, ); // Let mw_editPost() do all of the heavy lifting. return $this->mw_editPost( $args ); } /** * Retrieve page list. * * @since 2.2.0 * * @global wpdb $wpdb WordPress database abstraction object. * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type string $username * @type string $password * } * @return array|IXR_Error */ public function wp_getPageList( $args ) { global $wpdb; $this->escape( $args ); $username = $args[1]; $password = $args[2]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! current_user_can( 'edit_pages' ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit pages.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getPageList', $args, $this ); // Get list of page IDs and titles. $page_list = $wpdb->get_results( " SELECT ID page_id, post_title page_title, post_parent page_parent_id, post_date_gmt, post_date, post_status FROM {$wpdb->posts} WHERE post_type = 'page' ORDER BY ID " ); // The date needs to be formatted properly. $num_pages = count( $page_list ); for ( $i = 0; $i < $num_pages; $i++ ) { $page_list[ $i ]->dateCreated = $this->_convert_date( $page_list[ $i ]->post_date ); $page_list[ $i ]->date_created_gmt = $this->_convert_date_gmt( $page_list[ $i ]->post_date_gmt, $page_list[ $i ]->post_date ); unset( $page_list[ $i ]->post_date_gmt ); unset( $page_list[ $i ]->post_date ); unset( $page_list[ $i ]->post_status ); } return $page_list; } /** * Retrieve authors list. * * @since 2.2.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type string $username * @type string $password * } * @return array|IXR_Error */ public function wp_getAuthors( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! current_user_can( 'edit_posts' ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit posts.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getAuthors', $args, $this ); $authors = array(); foreach ( get_users( array( 'fields' => array( 'ID', 'user_login', 'display_name' ) ) ) as $user ) { $authors[] = array( 'user_id' => $user->ID, 'user_login' => $user->user_login, 'display_name' => $user->display_name, ); } return $authors; } /** * Get list of all tags * * @since 2.7.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type string $username * @type string $password * } * @return array|IXR_Error */ public function wp_getTags( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! current_user_can( 'edit_posts' ) ) { return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view tags.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getKeywords', $args, $this ); $tags = array(); $all_tags = get_tags(); if ( $all_tags ) { foreach ( (array) $all_tags as $tag ) { $struct = array(); $struct['tag_id'] = $tag->term_id; $struct['name'] = $tag->name; $struct['count'] = $tag->count; $struct['slug'] = $tag->slug; $struct['html_url'] = esc_html( get_tag_link( $tag->term_id ) ); $struct['rss_url'] = esc_html( get_tag_feed_link( $tag->term_id ) ); $tags[] = $struct; } } return $tags; } /** * Create new category. * * @since 2.2.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type string $username * @type string $password * @type array $category * } * @return int|IXR_Error Category ID. */ public function wp_newCategory( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $category = $args[3]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.newCategory', $args, $this ); // Make sure the user is allowed to add a category. if ( ! current_user_can( 'manage_categories' ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to add a category.' ) ); } // If no slug was provided, make it empty // so that WordPress will generate one. if ( empty( $category['slug'] ) ) { $category['slug'] = ''; } // If no parent_id was provided, make it empty // so that it will be a top-level page (no parent). if ( ! isset( $category['parent_id'] ) ) { $category['parent_id'] = ''; } // If no description was provided, make it empty. if ( empty( $category['description'] ) ) { $category['description'] = ''; } $new_category = array( 'cat_name' => $category['name'], 'category_nicename' => $category['slug'], 'category_parent' => $category['parent_id'], 'category_description' => $category['description'], ); $cat_id = wp_insert_category( $new_category, true ); if ( is_wp_error( $cat_id ) ) { if ( 'term_exists' === $cat_id->get_error_code() ) { return (int) $cat_id->get_error_data(); } else { return new IXR_Error( 500, __( 'Sorry, the category could not be created.' ) ); } } elseif ( ! $cat_id ) { return new IXR_Error( 500, __( 'Sorry, the category could not be created.' ) ); } /** * Fires after a new category has been successfully created via XML-RPC. * * @since 3.4.0 * * @param int $cat_id ID of the new category. * @param array $args An array of new category arguments. */ do_action( 'xmlrpc_call_success_wp_newCategory', $cat_id, $args ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase return $cat_id; } /** * Remove category. * * @since 2.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type string $username * @type string $password * @type int $category_id * } * @return bool|IXR_Error See wp_delete_term() for return info. */ public function wp_deleteCategory( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $category_id = (int) $args[3]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.deleteCategory', $args, $this ); if ( ! current_user_can( 'delete_term', $category_id ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to delete this category.' ) ); } $status = wp_delete_term( $category_id, 'category' ); if ( true == $status ) { /** * Fires after a category has been successfully deleted via XML-RPC. * * @since 3.4.0 * * @param int $category_id ID of the deleted category. * @param array $args An array of arguments to delete the category. */ do_action( 'xmlrpc_call_success_wp_deleteCategory', $category_id, $args ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase } return $status; } /** * Retrieve category list. * * @since 2.2.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type string $username * @type string $password * @type array $category * @type int $max_results * } * @return array|IXR_Error */ public function wp_suggestCategories( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $category = $args[3]; $max_results = (int) $args[4]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! current_user_can( 'edit_posts' ) ) { return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view categories.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.suggestCategories', $args, $this ); $category_suggestions = array(); $args = array( 'get' => 'all', 'number' => $max_results, 'name__like' => $category, ); foreach ( (array) get_categories( $args ) as $cat ) { $category_suggestions[] = array( 'category_id' => $cat->term_id, 'category_name' => $cat->name, ); } return $category_suggestions; } /** * Retrieve comment. * * @since 2.7.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type string $username * @type string $password * @type int $comment_id * } * @return array|IXR_Error */ public function wp_getComment( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $comment_id = (int) $args[3]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getComment', $args, $this ); $comment = get_comment( $comment_id ); if ( ! $comment ) { return new IXR_Error( 404, __( 'Invalid comment ID.' ) ); } if ( ! current_user_can( 'edit_comment', $comment_id ) ) { return new IXR_Error( 403, __( 'Sorry, you are not allowed to moderate or edit this comment.' ) ); } return $this->_prepare_comment( $comment ); } /** * Retrieve comments. * * Besides the common blog_id (unused), username, and password arguments, it takes a filter * array as last argument. * * Accepted 'filter' keys are 'status', 'post_id', 'offset', and 'number'. * * The defaults are as follows: * - 'status' - Default is ''. Filter by status (e.g., 'approve', 'hold') * - 'post_id' - Default is ''. The post where the comment is posted. Empty string shows all comments. * - 'number' - Default is 10. Total number of media items to retrieve. * - 'offset' - Default is 0. See WP_Query::query() for more. * * @since 2.7.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type string $username * @type string $password * @type array $struct * } * @return array|IXR_Error Contains a collection of comments. See wp_xmlrpc_server::wp_getComment() for a description of each item contents */ public function wp_getComments( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $struct = isset( $args[3] ) ? $args[3] : array(); $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getComments', $args, $this ); if ( isset( $struct['status'] ) ) { $status = $struct['status']; } else { $status = ''; } if ( ! current_user_can( 'moderate_comments' ) && 'approve' !== $status ) { return new IXR_Error( 401, __( 'Invalid comment status.' ) ); } $post_id = ''; if ( isset( $struct['post_id'] ) ) { $post_id = absint( $struct['post_id'] ); } $post_type = ''; if ( isset( $struct['post_type'] ) ) { $post_type_object = get_post_type_object( $struct['post_type'] ); if ( ! $post_type_object || ! post_type_supports( $post_type_object->name, 'comments' ) ) { return new IXR_Error( 404, __( 'Invalid post type.' ) ); } $post_type = $struct['post_type']; } $offset = 0; if ( isset( $struct['offset'] ) ) { $offset = absint( $struct['offset'] ); } $number = 10; if ( isset( $struct['number'] ) ) { $number = absint( $struct['number'] ); } $comments = get_comments( array( 'status' => $status, 'post_id' => $post_id, 'offset' => $offset, 'number' => $number, 'post_type' => $post_type, ) ); $comments_struct = array(); if ( is_array( $comments ) ) { foreach ( $comments as $comment ) { $comments_struct[] = $this->_prepare_comment( $comment ); } } return $comments_struct; } /** * Delete a comment. * * By default, the comment will be moved to the Trash instead of deleted. * See wp_delete_comment() for more information on this behavior. * * @since 2.7.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type string $username * @type string $password * @type int $comment_ID * } * @return bool|IXR_Error See wp_delete_comment(). */ public function wp_deleteComment( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $comment_ID = (int) $args[3]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! get_comment( $comment_ID ) ) { return new IXR_Error( 404, __( 'Invalid comment ID.' ) ); } if ( ! current_user_can( 'edit_comment', $comment_ID ) ) { return new IXR_Error( 403, __( 'Sorry, you are not allowed to delete this comment.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.deleteComment', $args, $this ); $status = wp_delete_comment( $comment_ID ); if ( $status ) { /** * Fires after a comment has been successfully deleted via XML-RPC. * * @since 3.4.0 * * @param int $comment_ID ID of the deleted comment. * @param array $args An array of arguments to delete the comment. */ do_action( 'xmlrpc_call_success_wp_deleteComment', $comment_ID, $args ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase } return $status; } /** * Edit comment. * * Besides the common blog_id (unused), username, and password arguments, it takes a * comment_id integer and a content_struct array as last argument. * * The allowed keys in the content_struct array are: * - 'author' * - 'author_url' * - 'author_email' * - 'content' * - 'date_created_gmt' * - 'status'. Common statuses are 'approve', 'hold', 'spam'. See get_comment_statuses() for more details * * @since 2.7.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type string $username * @type string $password * @type int $comment_ID * @type array $content_struct * } * @return true|IXR_Error True, on success. */ public function wp_editComment( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $comment_ID = (int) $args[3]; $content_struct = $args[4]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! get_comment( $comment_ID ) ) { return new IXR_Error( 404, __( 'Invalid comment ID.' ) ); } if ( ! current_user_can( 'edit_comment', $comment_ID ) ) { return new IXR_Error( 403, __( 'Sorry, you are not allowed to moderate or edit this comment.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.editComment', $args, $this ); $comment = array( 'comment_ID' => $comment_ID, ); if ( isset( $content_struct['status'] ) ) { $statuses = get_comment_statuses(); $statuses = array_keys( $statuses ); if ( ! in_array( $content_struct['status'], $statuses, true ) ) { return new IXR_Error( 401, __( 'Invalid comment status.' ) ); } $comment['comment_approved'] = $content_struct['status']; } // Do some timestamp voodoo. if ( ! empty( $content_struct['date_created_gmt'] ) ) { // We know this is supposed to be GMT, so we're going to slap that Z on there by force. $dateCreated = rtrim( $content_struct['date_created_gmt']->getIso(), 'Z' ) . 'Z'; $comment['comment_date'] = get_date_from_gmt( $dateCreated ); $comment['comment_date_gmt'] = iso8601_to_datetime( $dateCreated, 'gmt' ); } if ( isset( $content_struct['content'] ) ) { $comment['comment_content'] = $content_struct['content']; } if ( isset( $content_struct['author'] ) ) { $comment['comment_author'] = $content_struct['author']; } if ( isset( $content_struct['author_url'] ) ) { $comment['comment_author_url'] = $content_struct['author_url']; } if ( isset( $content_struct['author_email'] ) ) { $comment['comment_author_email'] = $content_struct['author_email']; } $result = wp_update_comment( $comment, true ); if ( is_wp_error( $result ) ) { return new IXR_Error( 500, $result->get_error_message() ); } if ( ! $result ) { return new IXR_Error( 500, __( 'Sorry, the comment could not be updated.' ) ); } /** * Fires after a comment has been successfully updated via XML-RPC. * * @since 3.4.0 * * @param int $comment_ID ID of the updated comment. * @param array $args An array of arguments to update the comment. */ do_action( 'xmlrpc_call_success_wp_editComment', $comment_ID, $args ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase return true; } /** * Create new comment. * * @since 2.7.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type string $username * @type string $password * @type string|int $post * @type array $content_struct * } * @return int|IXR_Error See wp_new_comment(). */ public function wp_newComment( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $post = $args[3]; $content_struct = $args[4]; /** * Filters whether to allow anonymous comments over XML-RPC. * * @since 2.7.0 * * @param bool $allow Whether to allow anonymous commenting via XML-RPC. * Default false. */ $allow_anon = apply_filters( 'xmlrpc_allow_anonymous_comments', false ); $user = $this->login( $username, $password ); if ( ! $user ) { $logged_in = false; if ( $allow_anon && get_option( 'comment_registration' ) ) { return new IXR_Error( 403, __( 'Sorry, you must be logged in to comment.' ) ); } elseif ( ! $allow_anon ) { return $this->error; } } else { $logged_in = true; } if ( is_numeric( $post ) ) { $post_id = absint( $post ); } else { $post_id = url_to_postid( $post ); } if ( ! $post_id ) { return new IXR_Error( 404, __( 'Invalid post ID.' ) ); } if ( ! get_post( $post_id ) ) { return new IXR_Error( 404, __( 'Invalid post ID.' ) ); } if ( ! comments_open( $post_id ) ) { return new IXR_Error( 403, __( 'Sorry, comments are closed for this item.' ) ); } if ( 'publish' === get_post_status( $post_id ) && ! current_user_can( 'edit_post', $post_id ) && post_password_required( $post_id ) ) { return new IXR_Error( 403, __( 'Sorry, you are not allowed to comment on this post.' ) ); } if ( 'private' === get_post_status( $post_id ) && ! current_user_can( 'read_post', $post_id ) ) { return new IXR_Error( 403, __( 'Sorry, you are not allowed to comment on this post.' ) ); } $comment = array( 'comment_post_ID' => $post_id, 'comment_content' => trim( $content_struct['content'] ), ); if ( $logged_in ) { $display_name = $user->display_name; $user_email = $user->user_email; $user_url = $user->user_url; $comment['comment_author'] = $this->escape( $display_name ); $comment['comment_author_email'] = $this->escape( $user_email ); $comment['comment_author_url'] = $this->escape( $user_url ); $comment['user_ID'] = $user->ID; } else { $comment['comment_author'] = ''; if ( isset( $content_struct['author'] ) ) { $comment['comment_author'] = $content_struct['author']; } $comment['comment_author_email'] = ''; if ( isset( $content_struct['author_email'] ) ) { $comment['comment_author_email'] = $content_struct['author_email']; } $comment['comment_author_url'] = ''; if ( isset( $content_struct['author_url'] ) ) { $comment['comment_author_url'] = $content_struct['author_url']; } $comment['user_ID'] = 0; if ( get_option( 'require_name_email' ) ) { if ( strlen( $comment['comment_author_email'] ) < 6 || '' === $comment['comment_author'] ) { return new IXR_Error( 403, __( 'Comment author name and email are required.' ) ); } elseif ( ! is_email( $comment['comment_author_email'] ) ) { return new IXR_Error( 403, __( 'A valid email address is required.' ) ); } } } $comment['comment_parent'] = isset( $content_struct['comment_parent'] ) ? absint( $content_struct['comment_parent'] ) : 0; /** This filter is documented in wp-includes/comment.php */ $allow_empty = apply_filters( 'allow_empty_comment', false, $comment ); if ( ! $allow_empty && '' === $comment['comment_content'] ) { return new IXR_Error( 403, __( 'Comment is required.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.newComment', $args, $this ); $comment_ID = wp_new_comment( $comment, true ); if ( is_wp_error( $comment_ID ) ) { return new IXR_Error( 403, $comment_ID->get_error_message() ); } if ( ! $comment_ID ) { return new IXR_Error( 403, __( 'Something went wrong.' ) ); } /** * Fires after a new comment has been successfully created via XML-RPC. * * @since 3.4.0 * * @param int $comment_ID ID of the new comment. * @param array $args An array of new comment arguments. */ do_action( 'xmlrpc_call_success_wp_newComment', $comment_ID, $args ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase return $comment_ID; } /** * Retrieve all of the comment status. * * @since 2.7.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type string $username * @type string $password * } * @return array|IXR_Error */ public function wp_getCommentStatusList( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! current_user_can( 'publish_posts' ) ) { return new IXR_Error( 403, __( 'Sorry, you are not allowed to access details about this site.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getCommentStatusList', $args, $this ); return get_comment_statuses(); } /** * Retrieve comment count. * * @since 2.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type string $username * @type string $password * @type int $post_id * } * @return array|IXR_Error */ public function wp_getCommentCount( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $post_id = (int) $args[3]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } $post = get_post( $post_id, ARRAY_A ); if ( empty( $post['ID'] ) ) { return new IXR_Error( 404, __( 'Invalid post ID.' ) ); } if ( ! current_user_can( 'edit_post', $post_id ) ) { return new IXR_Error( 403, __( 'Sorry, you are not allowed to access details of this post.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getCommentCount', $args, $this ); $count = wp_count_comments( $post_id ); return array( 'approved' => $count->approved, 'awaiting_moderation' => $count->moderated, 'spam' => $count->spam, 'total_comments' => $count->total_comments, ); } /** * Retrieve post statuses. * * @since 2.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type string $username * @type string $password * } * @return array|IXR_Error */ public function wp_getPostStatusList( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! current_user_can( 'edit_posts' ) ) { return new IXR_Error( 403, __( 'Sorry, you are not allowed to access details about this site.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getPostStatusList', $args, $this ); return get_post_statuses(); } /** * Retrieve page statuses. * * @since 2.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type string $username * @type string $password * } * @return array|IXR_Error */ public function wp_getPageStatusList( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! current_user_can( 'edit_pages' ) ) { return new IXR_Error( 403, __( 'Sorry, you are not allowed to access details about this site.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getPageStatusList', $args, $this ); return get_page_statuses(); } /** * Retrieve page templates. * * @since 2.6.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type string $username * @type string $password * } * @return array|IXR_Error */ public function wp_getPageTemplates( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! current_user_can( 'edit_pages' ) ) { return new IXR_Error( 403, __( 'Sorry, you are not allowed to access details about this site.' ) ); } $templates = get_page_templates(); $templates['Default'] = 'default'; return $templates; } /** * Retrieve blog options. * * @since 2.6.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type string $username * @type string $password * @type array $options * } * @return array|IXR_Error */ public function wp_getOptions( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $options = isset( $args[3] ) ? (array) $args[3] : array(); $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } // If no specific options where asked for, return all of them. if ( count( $options ) == 0 ) { $options = array_keys( $this->blog_options ); } return $this->_getOptions( $options ); } /** * Retrieve blog options value from list. * * @since 2.6.0 * * @param array $options Options to retrieve. * @return array */ public function _getOptions( $options ) { $data = array(); $can_manage = current_user_can( 'manage_options' ); foreach ( $options as $option ) { if ( array_key_exists( $option, $this->blog_options ) ) { $data[ $option ] = $this->blog_options[ $option ]; // Is the value static or dynamic? if ( isset( $data[ $option ]['option'] ) ) { $data[ $option ]['value'] = get_option( $data[ $option ]['option'] ); unset( $data[ $option ]['option'] ); } if ( ! $can_manage ) { $data[ $option ]['readonly'] = true; } } } return $data; } /** * Update blog options. * * @since 2.6.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type string $username * @type string $password * @type array $options * } * @return array|IXR_Error */ public function wp_setOptions( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $options = (array) $args[3]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! current_user_can( 'manage_options' ) ) { return new IXR_Error( 403, __( 'Sorry, you are not allowed to update options.' ) ); } $option_names = array(); foreach ( $options as $o_name => $o_value ) { $option_names[] = $o_name; if ( ! array_key_exists( $o_name, $this->blog_options ) ) { continue; } if ( true == $this->blog_options[ $o_name ]['readonly'] ) { continue; } update_option( $this->blog_options[ $o_name ]['option'], wp_unslash( $o_value ) ); } // Now return the updated values. return $this->_getOptions( $option_names ); } /** * Retrieve a media item by ID * * @since 3.1.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type string $username * @type string $password * @type int $attachment_id * } * @return array|IXR_Error Associative array contains: * - 'date_created_gmt' * - 'parent' * - 'link' * - 'thumbnail' * - 'title' * - 'caption' * - 'description' * - 'metadata' */ public function wp_getMediaItem( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $attachment_id = (int) $args[3]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! current_user_can( 'upload_files' ) ) { return new IXR_Error( 403, __( 'Sorry, you are not allowed to upload files.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getMediaItem', $args, $this ); $attachment = get_post( $attachment_id ); if ( ! $attachment || 'attachment' !== $attachment->post_type ) { return new IXR_Error( 404, __( 'Invalid attachment ID.' ) ); } return $this->_prepare_media_item( $attachment ); } /** * Retrieves a collection of media library items (or attachments) * * Besides the common blog_id (unused), username, and password arguments, it takes a filter * array as last argument. * * Accepted 'filter' keys are 'parent_id', 'mime_type', 'offset', and 'number'. * * The defaults are as follows: * - 'number' - Default is 5. Total number of media items to retrieve. * - 'offset' - Default is 0. See WP_Query::query() for more. * - 'parent_id' - Default is ''. The post where the media item is attached. Empty string shows all media items. 0 shows unattached media items. * - 'mime_type' - Default is ''. Filter by mime type (e.g., 'image/jpeg', 'application/pdf') * * @since 3.1.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type string $username * @type string $password * @type array $struct * } * @return array|IXR_Error Contains a collection of media items. See wp_xmlrpc_server::wp_getMediaItem() for a description of each item contents */ public function wp_getMediaLibrary( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $struct = isset( $args[3] ) ? $args[3] : array(); $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! current_user_can( 'upload_files' ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to upload files.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getMediaLibrary', $args, $this ); $parent_id = ( isset( $struct['parent_id'] ) ) ? absint( $struct['parent_id'] ) : ''; $mime_type = ( isset( $struct['mime_type'] ) ) ? $struct['mime_type'] : ''; $offset = ( isset( $struct['offset'] ) ) ? absint( $struct['offset'] ) : 0; $number = ( isset( $struct['number'] ) ) ? absint( $struct['number'] ) : -1; $attachments = get_posts( array( 'post_type' => 'attachment', 'post_parent' => $parent_id, 'offset' => $offset, 'numberposts' => $number, 'post_mime_type' => $mime_type, ) ); $attachments_struct = array(); foreach ( $attachments as $attachment ) { $attachments_struct[] = $this->_prepare_media_item( $attachment ); } return $attachments_struct; } /** * Retrieves a list of post formats used by the site. * * @since 3.1.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type string $username * @type string $password * } * @return array|IXR_Error List of post formats, otherwise IXR_Error object. */ public function wp_getPostFormats( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! current_user_can( 'edit_posts' ) ) { return new IXR_Error( 403, __( 'Sorry, you are not allowed to access details about this site.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getPostFormats', $args, $this ); $formats = get_post_format_strings(); // Find out if they want a list of currently supports formats. if ( isset( $args[3] ) && is_array( $args[3] ) ) { if ( $args[3]['show-supported'] ) { if ( current_theme_supports( 'post-formats' ) ) { $supported = get_theme_support( 'post-formats' ); $data = array(); $data['all'] = $formats; $data['supported'] = $supported[0]; $formats = $data; } } } return $formats; } /** * Retrieves a post type * * @since 3.4.0 * * @see get_post_type_object() * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type string $username * @type string $password * @type string $post_type_name * @type array $fields (optional) * } * @return array|IXR_Error Array contains: * - 'labels' * - 'description' * - 'capability_type' * - 'cap' * - 'map_meta_cap' * - 'hierarchical' * - 'menu_position' * - 'taxonomies' * - 'supports' */ public function wp_getPostType( $args ) { if ( ! $this->minimum_args( $args, 4 ) ) { return $this->error; } $this->escape( $args ); $username = $args[1]; $password = $args[2]; $post_type_name = $args[3]; if ( isset( $args[4] ) ) { $fields = $args[4]; } else { /** * Filters the default query fields used by the given XML-RPC method. * * @since 3.4.0 * * @param array $fields An array of post type query fields for the given method. * @param string $method The method name. */ $fields = apply_filters( 'xmlrpc_default_posttype_fields', array( 'labels', 'cap', 'taxonomies' ), 'wp.getPostType' ); } $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getPostType', $args, $this ); if ( ! post_type_exists( $post_type_name ) ) { return new IXR_Error( 403, __( 'Invalid post type.' ) ); } $post_type = get_post_type_object( $post_type_name ); if ( ! current_user_can( $post_type->cap->edit_posts ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit posts in this post type.' ) ); } return $this->_prepare_post_type( $post_type, $fields ); } /** * Retrieves a post types * * @since 3.4.0 * * @see get_post_types() * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type string $username * @type string $password * @type array $filter (optional) * @type array $fields (optional) * } * @return array|IXR_Error */ public function wp_getPostTypes( $args ) { if ( ! $this->minimum_args( $args, 3 ) ) { return $this->error; } $this->escape( $args ); $username = $args[1]; $password = $args[2]; $filter = isset( $args[3] ) ? $args[3] : array( 'public' => true ); if ( isset( $args[4] ) ) { $fields = $args[4]; } else { /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ $fields = apply_filters( 'xmlrpc_default_posttype_fields', array( 'labels', 'cap', 'taxonomies' ), 'wp.getPostTypes' ); } $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getPostTypes', $args, $this ); $post_types = get_post_types( $filter, 'objects' ); $struct = array(); foreach ( $post_types as $post_type ) { if ( ! current_user_can( $post_type->cap->edit_posts ) ) { continue; } $struct[ $post_type->name ] = $this->_prepare_post_type( $post_type, $fields ); } return $struct; } /** * Retrieve revisions for a specific post. * * @since 3.5.0 * * The optional $fields parameter specifies what fields will be included * in the response array. * * @uses wp_get_post_revisions() * @see wp_getPost() for more on $fields * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type string $username * @type string $password * @type int $post_id * @type array $fields (optional) * } * @return array|IXR_Error contains a collection of posts. */ public function wp_getRevisions( $args ) { if ( ! $this->minimum_args( $args, 4 ) ) { return $this->error; } $this->escape( $args ); $username = $args[1]; $password = $args[2]; $post_id = (int) $args[3]; if ( isset( $args[4] ) ) { $fields = $args[4]; } else { /** * Filters the default revision query fields used by the given XML-RPC method. * * @since 3.5.0 * * @param array $field An array of revision query fields. * @param string $method The method name. */ $fields = apply_filters( 'xmlrpc_default_revision_fields', array( 'post_date', 'post_date_gmt' ), 'wp.getRevisions' ); } $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getRevisions', $args, $this ); $post = get_post( $post_id ); if ( ! $post ) { return new IXR_Error( 404, __( 'Invalid post ID.' ) ); } if ( ! current_user_can( 'edit_post', $post_id ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit posts.' ) ); } // Check if revisions are enabled. if ( ! wp_revisions_enabled( $post ) ) { return new IXR_Error( 401, __( 'Sorry, revisions are disabled.' ) ); } $revisions = wp_get_post_revisions( $post_id ); if ( ! $revisions ) { return array(); } $struct = array(); foreach ( $revisions as $revision ) { if ( ! current_user_can( 'read_post', $revision->ID ) ) { continue; } // Skip autosaves. if ( wp_is_post_autosave( $revision ) ) { continue; } $struct[] = $this->_prepare_post( get_object_vars( $revision ), $fields ); } return $struct; } /** * Restore a post revision * * @since 3.5.0 * * @uses wp_restore_post_revision() * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type string $username * @type string $password * @type int $revision_id * } * @return bool|IXR_Error false if there was an error restoring, true if success. */ public function wp_restoreRevision( $args ) { if ( ! $this->minimum_args( $args, 3 ) ) { return $this->error; } $this->escape( $args ); $username = $args[1]; $password = $args[2]; $revision_id = (int) $args[3]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.restoreRevision', $args, $this ); $revision = wp_get_post_revision( $revision_id ); if ( ! $revision ) { return new IXR_Error( 404, __( 'Invalid post ID.' ) ); } if ( wp_is_post_autosave( $revision ) ) { return new IXR_Error( 404, __( 'Invalid post ID.' ) ); } $post = get_post( $revision->post_parent ); if ( ! $post ) { return new IXR_Error( 404, __( 'Invalid post ID.' ) ); } if ( ! current_user_can( 'edit_post', $revision->post_parent ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) ); } // Check if revisions are disabled. if ( ! wp_revisions_enabled( $post ) ) { return new IXR_Error( 401, __( 'Sorry, revisions are disabled.' ) ); } $post = wp_restore_post_revision( $revision_id ); return (bool) $post; } /* * Blogger API functions. * Specs on http://plant.blogger.com/api and https://groups.yahoo.com/group/bloggerDev/ */ /** * Retrieve blogs that user owns. * * Will make more sense once we support multiple blogs. * * @since 1.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type string $username * @type string $password * } * @return array|IXR_Error */ public function blogger_getUsersBlogs( $args ) { if ( ! $this->minimum_args( $args, 3 ) ) { return $this->error; } if ( is_multisite() ) { return $this->_multisite_getUsersBlogs( $args ); } $this->escape( $args ); $username = $args[1]; $password = $args[2]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'blogger.getUsersBlogs', $args, $this ); $is_admin = current_user_can( 'manage_options' ); $struct = array( 'isAdmin' => $is_admin, 'url' => get_option( 'home' ) . '/', 'blogid' => '1', 'blogName' => get_option( 'blogname' ), 'xmlrpc' => site_url( 'xmlrpc.php', 'rpc' ), ); return array( $struct ); } /** * Private function for retrieving a users blogs for multisite setups * * @since 3.0.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type string $username Username. * @type string $password Password. * } * @return array|IXR_Error */ protected function _multisite_getUsersBlogs( $args ) { $current_blog = get_site(); $domain = $current_blog->domain; $path = $current_blog->path . 'xmlrpc.php'; $rpc = new IXR_Client( set_url_scheme( "http://{$domain}{$path}" ) ); $rpc->query( 'wp.getUsersBlogs', $args[1], $args[2] ); $blogs = $rpc->getResponse(); if ( isset( $blogs['faultCode'] ) ) { return new IXR_Error( $blogs['faultCode'], $blogs['faultString'] ); } if ( $_SERVER['HTTP_HOST'] == $domain && $_SERVER['REQUEST_URI'] == $path ) { return $blogs; } else { foreach ( (array) $blogs as $blog ) { if ( strpos( $blog['url'], $_SERVER['HTTP_HOST'] ) ) { return array( $blog ); } } return array(); } } /** * Retrieve user's data. * * Gives your client some info about you, so you don't have to. * * @since 1.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type string $username * @type string $password * } * @return array|IXR_Error */ public function blogger_getUserInfo( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! current_user_can( 'edit_posts' ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to access user data on this site.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'blogger.getUserInfo', $args, $this ); $struct = array( 'nickname' => $user->nickname, 'userid' => $user->ID, 'url' => $user->user_url, 'lastname' => $user->last_name, 'firstname' => $user->first_name, ); return $struct; } /** * Retrieve post. * * @since 1.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type int $post_ID * @type string $username * @type string $password * } * @return array|IXR_Error */ public function blogger_getPost( $args ) { $this->escape( $args ); $post_ID = (int) $args[1]; $username = $args[2]; $password = $args[3]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } $post_data = get_post( $post_ID, ARRAY_A ); if ( ! $post_data ) { return new IXR_Error( 404, __( 'Invalid post ID.' ) ); } if ( ! current_user_can( 'edit_post', $post_ID ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'blogger.getPost', $args, $this ); $categories = implode( ',', wp_get_post_categories( $post_ID ) ); $content = '<title>' . wp_unslash( $post_data['post_title'] ) . '</title>'; $content .= '<category>' . $categories . '</category>'; $content .= wp_unslash( $post_data['post_content'] ); $struct = array( 'userid' => $post_data['post_author'], 'dateCreated' => $this->_convert_date( $post_data['post_date'] ), 'content' => $content, 'postid' => (string) $post_data['ID'], ); return $struct; } /** * Retrieve list of recent posts. * * @since 1.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type string $appkey (unused) * @type int $blog_id (unused) * @type string $username * @type string $password * @type int $numberposts (optional) * } * @return array|IXR_Error */ public function blogger_getRecentPosts( $args ) { $this->escape( $args ); // $args[0] = appkey - ignored. $username = $args[2]; $password = $args[3]; if ( isset( $args[4] ) ) { $query = array( 'numberposts' => absint( $args[4] ) ); } else { $query = array(); } $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! current_user_can( 'edit_posts' ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit posts.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'blogger.getRecentPosts', $args, $this ); $posts_list = wp_get_recent_posts( $query ); if ( ! $posts_list ) { $this->error = new IXR_Error( 500, __( 'Either there are no posts, or something went wrong.' ) ); return $this->error; } $recent_posts = array(); foreach ( $posts_list as $entry ) { if ( ! current_user_can( 'edit_post', $entry['ID'] ) ) { continue; } $post_date = $this->_convert_date( $entry['post_date'] ); $categories = implode( ',', wp_get_post_categories( $entry['ID'] ) ); $content = '<title>' . wp_unslash( $entry['post_title'] ) . '</title>'; $content .= '<category>' . $categories . '</category>'; $content .= wp_unslash( $entry['post_content'] ); $recent_posts[] = array( 'userid' => $entry['post_author'], 'dateCreated' => $post_date, 'content' => $content, 'postid' => (string) $entry['ID'], ); } return $recent_posts; } /** * Deprecated. * * @since 1.5.0 * @deprecated 3.5.0 * * @param array $args Unused. * @return IXR_Error Error object. */ public function blogger_getTemplate( $args ) { return new IXR_Error( 403, __( 'Sorry, this method is not supported.' ) ); } /** * Deprecated. * * @since 1.5.0 * @deprecated 3.5.0 * * @param array $args Unused. * @return IXR_Error Error object. */ public function blogger_setTemplate( $args ) { return new IXR_Error( 403, __( 'Sorry, this method is not supported.' ) ); } /** * Creates new post. * * @since 1.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type string $appkey (unused) * @type int $blog_id (unused) * @type string $username * @type string $password * @type string $content * @type string $publish * } * @return int|IXR_Error */ public function blogger_newPost( $args ) { $this->escape( $args ); $username = $args[2]; $password = $args[3]; $content = $args[4]; $publish = $args[5]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'blogger.newPost', $args, $this ); $cap = ( $publish ) ? 'publish_posts' : 'edit_posts'; if ( ! current_user_can( get_post_type_object( 'post' )->cap->create_posts ) || ! current_user_can( $cap ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to post on this site.' ) ); } $post_status = ( $publish ) ? 'publish' : 'draft'; $post_author = $user->ID; $post_title = xmlrpc_getposttitle( $content ); $post_category = xmlrpc_getpostcategory( $content ); $post_content = xmlrpc_removepostdata( $content ); $post_date = current_time( 'mysql' ); $post_date_gmt = current_time( 'mysql', 1 ); $post_data = compact( 'post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_title', 'post_category', 'post_status' ); $post_ID = wp_insert_post( $post_data ); if ( is_wp_error( $post_ID ) ) { return new IXR_Error( 500, $post_ID->get_error_message() ); } if ( ! $post_ID ) { return new IXR_Error( 500, __( 'Sorry, the post could not be created.' ) ); } $this->attach_uploads( $post_ID, $post_content ); /** * Fires after a new post has been successfully created via the XML-RPC Blogger API. * * @since 3.4.0 * * @param int $post_ID ID of the new post. * @param array $args An array of new post arguments. */ do_action( 'xmlrpc_call_success_blogger_newPost', $post_ID, $args ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase return $post_ID; } /** * Edit a post. * * @since 1.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type int $post_ID * @type string $username * @type string $password * @type string $content * @type bool $publish * } * @return true|IXR_Error true when done. */ public function blogger_editPost( $args ) { $this->escape( $args ); $post_ID = (int) $args[1]; $username = $args[2]; $password = $args[3]; $content = $args[4]; $publish = $args[5]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'blogger.editPost', $args, $this ); $actual_post = get_post( $post_ID, ARRAY_A ); if ( ! $actual_post || 'post' !== $actual_post['post_type'] ) { return new IXR_Error( 404, __( 'Sorry, no such post.' ) ); } $this->escape( $actual_post ); if ( ! current_user_can( 'edit_post', $post_ID ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) ); } if ( 'publish' === $actual_post['post_status'] && ! current_user_can( 'publish_posts' ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to publish this post.' ) ); } $postdata = array(); $postdata['ID'] = $actual_post['ID']; $postdata['post_content'] = xmlrpc_removepostdata( $content ); $postdata['post_title'] = xmlrpc_getposttitle( $content ); $postdata['post_category'] = xmlrpc_getpostcategory( $content ); $postdata['post_status'] = $actual_post['post_status']; $postdata['post_excerpt'] = $actual_post['post_excerpt']; $postdata['post_status'] = $publish ? 'publish' : 'draft'; $result = wp_update_post( $postdata ); if ( ! $result ) { return new IXR_Error( 500, __( 'Sorry, the post could not be updated.' ) ); } $this->attach_uploads( $actual_post['ID'], $postdata['post_content'] ); /** * Fires after a post has been successfully updated via the XML-RPC Blogger API. * * @since 3.4.0 * * @param int $post_ID ID of the updated post. * @param array $args An array of arguments for the post to edit. */ do_action( 'xmlrpc_call_success_blogger_editPost', $post_ID, $args ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase return true; } /** * Remove a post. * * @since 1.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type int $post_ID * @type string $username * @type string $password * } * @return true|IXR_Error True when post is deleted. */ public function blogger_deletePost( $args ) { $this->escape( $args ); $post_ID = (int) $args[1]; $username = $args[2]; $password = $args[3]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'blogger.deletePost', $args, $this ); $actual_post = get_post( $post_ID, ARRAY_A ); if ( ! $actual_post || 'post' !== $actual_post['post_type'] ) { return new IXR_Error( 404, __( 'Sorry, no such post.' ) ); } if ( ! current_user_can( 'delete_post', $post_ID ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to delete this post.' ) ); } $result = wp_delete_post( $post_ID ); if ( ! $result ) { return new IXR_Error( 500, __( 'Sorry, the post could not be deleted.' ) ); } /** * Fires after a post has been successfully deleted via the XML-RPC Blogger API. * * @since 3.4.0 * * @param int $post_ID ID of the deleted post. * @param array $args An array of arguments to delete the post. */ do_action( 'xmlrpc_call_success_blogger_deletePost', $post_ID, $args ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase return true; } /* * MetaWeblog API functions. * Specs on wherever Dave Winer wants them to be. */ /** * Create a new post. * * The 'content_struct' argument must contain: * - title * - description * - mt_excerpt * - mt_text_more * - mt_keywords * - mt_tb_ping_urls * - categories * * Also, it can optionally contain: * - wp_slug * - wp_password * - wp_page_parent_id * - wp_page_order * - wp_author_id * - post_status | page_status - can be 'draft', 'private', 'publish', or 'pending' * - mt_allow_comments - can be 'open' or 'closed' * - mt_allow_pings - can be 'open' or 'closed' * - date_created_gmt * - dateCreated * - wp_post_thumbnail * * @since 1.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type string $username * @type string $password * @type array $content_struct * @type int $publish * } * @return int|IXR_Error */ public function mw_newPost( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $content_struct = $args[3]; $publish = isset( $args[4] ) ? $args[4] : 0; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'metaWeblog.newPost', $args, $this ); $page_template = ''; if ( ! empty( $content_struct['post_type'] ) ) { if ( 'page' === $content_struct['post_type'] ) { if ( $publish ) { $cap = 'publish_pages'; } elseif ( isset( $content_struct['page_status'] ) && 'publish' === $content_struct['page_status'] ) { $cap = 'publish_pages'; } else { $cap = 'edit_pages'; } $error_message = __( 'Sorry, you are not allowed to publish pages on this site.' ); $post_type = 'page'; if ( ! empty( $content_struct['wp_page_template'] ) ) { $page_template = $content_struct['wp_page_template']; } } elseif ( 'post' === $content_struct['post_type'] ) { if ( $publish ) { $cap = 'publish_posts'; } elseif ( isset( $content_struct['post_status'] ) && 'publish' === $content_struct['post_status'] ) { $cap = 'publish_posts'; } else { $cap = 'edit_posts'; } $error_message = __( 'Sorry, you are not allowed to publish posts on this site.' ); $post_type = 'post'; } else { // No other 'post_type' values are allowed here. return new IXR_Error( 401, __( 'Invalid post type.' ) ); } } else { if ( $publish ) { $cap = 'publish_posts'; } elseif ( isset( $content_struct['post_status'] ) && 'publish' === $content_struct['post_status'] ) { $cap = 'publish_posts'; } else { $cap = 'edit_posts'; } $error_message = __( 'Sorry, you are not allowed to publish posts on this site.' ); $post_type = 'post'; } if ( ! current_user_can( get_post_type_object( $post_type )->cap->create_posts ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to publish posts on this site.' ) ); } if ( ! current_user_can( $cap ) ) { return new IXR_Error( 401, $error_message ); } // Check for a valid post format if one was given. if ( isset( $content_struct['wp_post_format'] ) ) { $content_struct['wp_post_format'] = sanitize_key( $content_struct['wp_post_format'] ); if ( ! array_key_exists( $content_struct['wp_post_format'], get_post_format_strings() ) ) { return new IXR_Error( 404, __( 'Invalid post format.' ) ); } } // Let WordPress generate the 'post_name' (slug) unless // one has been provided. $post_name = ''; if ( isset( $content_struct['wp_slug'] ) ) { $post_name = $content_struct['wp_slug']; } // Only use a password if one was given. if ( isset( $content_struct['wp_password'] ) ) { $post_password = $content_struct['wp_password']; } else { $post_password = ''; } // Only set a post parent if one was given. if ( isset( $content_struct['wp_page_parent_id'] ) ) { $post_parent = $content_struct['wp_page_parent_id']; } else { $post_parent = 0; } // Only set the 'menu_order' if it was given. if ( isset( $content_struct['wp_page_order'] ) ) { $menu_order = $content_struct['wp_page_order']; } else { $menu_order = 0; } $post_author = $user->ID; // If an author id was provided then use it instead. if ( isset( $content_struct['wp_author_id'] ) && ( $user->ID != $content_struct['wp_author_id'] ) ) { switch ( $post_type ) { case 'post': if ( ! current_user_can( 'edit_others_posts' ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to create posts as this user.' ) ); } break; case 'page': if ( ! current_user_can( 'edit_others_pages' ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to create pages as this user.' ) ); } break; default: return new IXR_Error( 401, __( 'Invalid post type.' ) ); } $author = get_userdata( $content_struct['wp_author_id'] ); if ( ! $author ) { return new IXR_Error( 404, __( 'Invalid author ID.' ) ); } $post_author = $content_struct['wp_author_id']; } $post_title = isset( $content_struct['title'] ) ? $content_struct['title'] : null; $post_content = isset( $content_struct['description'] ) ? $content_struct['description'] : null; $post_status = $publish ? 'publish' : 'draft'; if ( isset( $content_struct[ "{$post_type}_status" ] ) ) { switch ( $content_struct[ "{$post_type}_status" ] ) { case 'draft': case 'pending': case 'private': case 'publish': $post_status = $content_struct[ "{$post_type}_status" ]; break; default: $post_status = $publish ? 'publish' : 'draft'; break; } } $post_excerpt = isset( $content_struct['mt_excerpt'] ) ? $content_struct['mt_excerpt'] : null; $post_more = isset( $content_struct['mt_text_more'] ) ? $content_struct['mt_text_more'] : null; $tags_input = isset( $content_struct['mt_keywords'] ) ? $content_struct['mt_keywords'] : null; if ( isset( $content_struct['mt_allow_comments'] ) ) { if ( ! is_numeric( $content_struct['mt_allow_comments'] ) ) { switch ( $content_struct['mt_allow_comments'] ) { case 'closed': $comment_status = 'closed'; break; case 'open': $comment_status = 'open'; break; default: $comment_status = get_default_comment_status( $post_type ); break; } } else { switch ( (int) $content_struct['mt_allow_comments'] ) { case 0: case 2: $comment_status = 'closed'; break; case 1: $comment_status = 'open'; break; default: $comment_status = get_default_comment_status( $post_type ); break; } } } else { $comment_status = get_default_comment_status( $post_type ); } if ( isset( $content_struct['mt_allow_pings'] ) ) { if ( ! is_numeric( $content_struct['mt_allow_pings'] ) ) { switch ( $content_struct['mt_allow_pings'] ) { case 'closed': $ping_status = 'closed'; break; case 'open': $ping_status = 'open'; break; default: $ping_status = get_default_comment_status( $post_type, 'pingback' ); break; } } else { switch ( (int) $content_struct['mt_allow_pings'] ) { case 0: $ping_status = 'closed'; break; case 1: $ping_status = 'open'; break; default: $ping_status = get_default_comment_status( $post_type, 'pingback' ); break; } } } else { $ping_status = get_default_comment_status( $post_type, 'pingback' ); } if ( $post_more ) { $post_content = $post_content . '<!--more-->' . $post_more; } $to_ping = null; if ( isset( $content_struct['mt_tb_ping_urls'] ) ) { $to_ping = $content_struct['mt_tb_ping_urls']; if ( is_array( $to_ping ) ) { $to_ping = implode( ' ', $to_ping ); } } // Do some timestamp voodoo. if ( ! empty( $content_struct['date_created_gmt'] ) ) { // We know this is supposed to be GMT, so we're going to slap that Z on there by force. $dateCreated = rtrim( $content_struct['date_created_gmt']->getIso(), 'Z' ) . 'Z'; } elseif ( ! empty( $content_struct['dateCreated'] ) ) { $dateCreated = $content_struct['dateCreated']->getIso(); } if ( ! empty( $dateCreated ) ) { $post_date = iso8601_to_datetime( $dateCreated ); $post_date_gmt = iso8601_to_datetime( $dateCreated, 'gmt' ); } else { $post_date = ''; $post_date_gmt = ''; } $post_category = array(); if ( isset( $content_struct['categories'] ) ) { $catnames = $content_struct['categories']; if ( is_array( $catnames ) ) { foreach ( $catnames as $cat ) { $post_category[] = get_cat_ID( $cat ); } } } $postdata = compact( 'post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt', 'comment_status', 'ping_status', 'to_ping', 'post_type', 'post_name', 'post_password', 'post_parent', 'menu_order', 'tags_input', 'page_template' ); $post_ID = get_default_post_to_edit( $post_type, true )->ID; $postdata['ID'] = $post_ID; // Only posts can be sticky. if ( 'post' === $post_type && isset( $content_struct['sticky'] ) ) { $data = $postdata; $data['sticky'] = $content_struct['sticky']; $error = $this->_toggle_sticky( $data ); if ( $error ) { return $error; } } if ( isset( $content_struct['custom_fields'] ) ) { $this->set_custom_fields( $post_ID, $content_struct['custom_fields'] ); } if ( isset( $content_struct['wp_post_thumbnail'] ) ) { if ( set_post_thumbnail( $post_ID, $content_struct['wp_post_thumbnail'] ) === false ) { return new IXR_Error( 404, __( 'Invalid attachment ID.' ) ); } unset( $content_struct['wp_post_thumbnail'] ); } // Handle enclosures. $thisEnclosure = isset( $content_struct['enclosure'] ) ? $content_struct['enclosure'] : null; $this->add_enclosure_if_new( $post_ID, $thisEnclosure ); $this->attach_uploads( $post_ID, $post_content ); // Handle post formats if assigned, value is validated earlier // in this function. if ( isset( $content_struct['wp_post_format'] ) ) { set_post_format( $post_ID, $content_struct['wp_post_format'] ); } $post_ID = wp_insert_post( $postdata, true ); if ( is_wp_error( $post_ID ) ) { return new IXR_Error( 500, $post_ID->get_error_message() ); } if ( ! $post_ID ) { return new IXR_Error( 500, __( 'Sorry, the post could not be created.' ) ); } /** * Fires after a new post has been successfully created via the XML-RPC MovableType API. * * @since 3.4.0 * * @param int $post_ID ID of the new post. * @param array $args An array of arguments to create the new post. */ do_action( 'xmlrpc_call_success_mw_newPost', $post_ID, $args ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase return (string) $post_ID; } /** * Adds an enclosure to a post if it's new. * * @since 2.8.0 * * @param int $post_ID Post ID. * @param array $enclosure Enclosure data. */ public function add_enclosure_if_new( $post_ID, $enclosure ) { if ( is_array( $enclosure ) && isset( $enclosure['url'] ) && isset( $enclosure['length'] ) && isset( $enclosure['type'] ) ) { $encstring = $enclosure['url'] . "\n" . $enclosure['length'] . "\n" . $enclosure['type'] . "\n"; $found = false; $enclosures = get_post_meta( $post_ID, 'enclosure' ); if ( $enclosures ) { foreach ( $enclosures as $enc ) { // This method used to omit the trailing new line. #23219 if ( rtrim( $enc, "\n" ) == rtrim( $encstring, "\n" ) ) { $found = true; break; } } } if ( ! $found ) { add_post_meta( $post_ID, 'enclosure', $encstring ); } } } /** * Attach upload to a post. * * @since 2.1.0 * * @global wpdb $wpdb WordPress database abstraction object. * * @param int $post_ID Post ID. * @param string $post_content Post Content for attachment. */ public function attach_uploads( $post_ID, $post_content ) { global $wpdb; // Find any unattached files. $attachments = $wpdb->get_results( "SELECT ID, guid FROM {$wpdb->posts} WHERE post_parent = '0' AND post_type = 'attachment'" ); if ( is_array( $attachments ) ) { foreach ( $attachments as $file ) { if ( ! empty( $file->guid ) && strpos( $post_content, $file->guid ) !== false ) { $wpdb->update( $wpdb->posts, array( 'post_parent' => $post_ID ), array( 'ID' => $file->ID ) ); } } } } /** * Edit a post. * * @since 1.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type string $username * @type string $password * @type array $content_struct * @type int $publish * } * @return true|IXR_Error True on success. */ public function mw_editPost( $args ) { $this->escape( $args ); $post_ID = (int) $args[0]; $username = $args[1]; $password = $args[2]; $content_struct = $args[3]; $publish = isset( $args[4] ) ? $args[4] : 0; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'metaWeblog.editPost', $args, $this ); $postdata = get_post( $post_ID, ARRAY_A ); /* * If there is no post data for the give post ID, stop now and return an error. * Otherwise a new post will be created (which was the old behavior). */ if ( ! $postdata || empty( $postdata['ID'] ) ) { return new IXR_Error( 404, __( 'Invalid post ID.' ) ); } if ( ! current_user_can( 'edit_post', $post_ID ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) ); } // Use wp.editPost to edit post types other than post and page. if ( ! in_array( $postdata['post_type'], array( 'post', 'page' ), true ) ) { return new IXR_Error( 401, __( 'Invalid post type.' ) ); } // Thwart attempt to change the post type. if ( ! empty( $content_struct['post_type'] ) && ( $content_struct['post_type'] != $postdata['post_type'] ) ) { return new IXR_Error( 401, __( 'The post type may not be changed.' ) ); } // Check for a valid post format if one was given. if ( isset( $content_struct['wp_post_format'] ) ) { $content_struct['wp_post_format'] = sanitize_key( $content_struct['wp_post_format'] ); if ( ! array_key_exists( $content_struct['wp_post_format'], get_post_format_strings() ) ) { return new IXR_Error( 404, __( 'Invalid post format.' ) ); } } $this->escape( $postdata ); $ID = $postdata['ID']; $post_content = $postdata['post_content']; $post_title = $postdata['post_title']; $post_excerpt = $postdata['post_excerpt']; $post_password = $postdata['post_password']; $post_parent = $postdata['post_parent']; $post_type = $postdata['post_type']; $menu_order = $postdata['menu_order']; $ping_status = $postdata['ping_status']; $comment_status = $postdata['comment_status']; // Let WordPress manage slug if none was provided. $post_name = $postdata['post_name']; if ( isset( $content_struct['wp_slug'] ) ) { $post_name = $content_struct['wp_slug']; } // Only use a password if one was given. if ( isset( $content_struct['wp_password'] ) ) { $post_password = $content_struct['wp_password']; } // Only set a post parent if one was given. if ( isset( $content_struct['wp_page_parent_id'] ) ) { $post_parent = $content_struct['wp_page_parent_id']; } // Only set the 'menu_order' if it was given. if ( isset( $content_struct['wp_page_order'] ) ) { $menu_order = $content_struct['wp_page_order']; } $page_template = null; if ( ! empty( $content_struct['wp_page_template'] ) && 'page' === $post_type ) { $page_template = $content_struct['wp_page_template']; } $post_author = $postdata['post_author']; // If an author id was provided then use it instead. if ( isset( $content_struct['wp_author_id'] ) ) { // Check permissions if attempting to switch author to or from another user. if ( $user->ID != $content_struct['wp_author_id'] || $user->ID != $post_author ) { switch ( $post_type ) { case 'post': if ( ! current_user_can( 'edit_others_posts' ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to change the post author as this user.' ) ); } break; case 'page': if ( ! current_user_can( 'edit_others_pages' ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to change the page author as this user.' ) ); } break; default: return new IXR_Error( 401, __( 'Invalid post type.' ) ); } $post_author = $content_struct['wp_author_id']; } } if ( isset( $content_struct['mt_allow_comments'] ) ) { if ( ! is_numeric( $content_struct['mt_allow_comments'] ) ) { switch ( $content_struct['mt_allow_comments'] ) { case 'closed': $comment_status = 'closed'; break; case 'open': $comment_status = 'open'; break; default: $comment_status = get_default_comment_status( $post_type ); break; } } else { switch ( (int) $content_struct['mt_allow_comments'] ) { case 0: case 2: $comment_status = 'closed'; break; case 1: $comment_status = 'open'; break; default: $comment_status = get_default_comment_status( $post_type ); break; } } } if ( isset( $content_struct['mt_allow_pings'] ) ) { if ( ! is_numeric( $content_struct['mt_allow_pings'] ) ) { switch ( $content_struct['mt_allow_pings'] ) { case 'closed': $ping_status = 'closed'; break; case 'open': $ping_status = 'open'; break; default: $ping_status = get_default_comment_status( $post_type, 'pingback' ); break; } } else { switch ( (int) $content_struct['mt_allow_pings'] ) { case 0: $ping_status = 'closed'; break; case 1: $ping_status = 'open'; break; default: $ping_status = get_default_comment_status( $post_type, 'pingback' ); break; } } } if ( isset( $content_struct['title'] ) ) { $post_title = $content_struct['title']; } if ( isset( $content_struct['description'] ) ) { $post_content = $content_struct['description']; } $post_category = array(); if ( isset( $content_struct['categories'] ) ) { $catnames = $content_struct['categories']; if ( is_array( $catnames ) ) { foreach ( $catnames as $cat ) { $post_category[] = get_cat_ID( $cat ); } } } if ( isset( $content_struct['mt_excerpt'] ) ) { $post_excerpt = $content_struct['mt_excerpt']; } $post_more = isset( $content_struct['mt_text_more'] ) ? $content_struct['mt_text_more'] : null; $post_status = $publish ? 'publish' : 'draft'; if ( isset( $content_struct[ "{$post_type}_status" ] ) ) { switch ( $content_struct[ "{$post_type}_status" ] ) { case 'draft': case 'pending': case 'private': case 'publish': $post_status = $content_struct[ "{$post_type}_status" ]; break; default: $post_status = $publish ? 'publish' : 'draft'; break; } } $tags_input = isset( $content_struct['mt_keywords'] ) ? $content_struct['mt_keywords'] : null; if ( 'publish' === $post_status || 'private' === $post_status ) { if ( 'page' === $post_type && ! current_user_can( 'publish_pages' ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to publish this page.' ) ); } elseif ( ! current_user_can( 'publish_posts' ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to publish this post.' ) ); } } if ( $post_more ) { $post_content = $post_content . '<!--more-->' . $post_more; } $to_ping = null; if ( isset( $content_struct['mt_tb_ping_urls'] ) ) { $to_ping = $content_struct['mt_tb_ping_urls']; if ( is_array( $to_ping ) ) { $to_ping = implode( ' ', $to_ping ); } } // Do some timestamp voodoo. if ( ! empty( $content_struct['date_created_gmt'] ) ) { // We know this is supposed to be GMT, so we're going to slap that Z on there by force. $dateCreated = rtrim( $content_struct['date_created_gmt']->getIso(), 'Z' ) . 'Z'; } elseif ( ! empty( $content_struct['dateCreated'] ) ) { $dateCreated = $content_struct['dateCreated']->getIso(); } // Default to not flagging the post date to be edited unless it's intentional. $edit_date = false; if ( ! empty( $dateCreated ) ) { $post_date = iso8601_to_datetime( $dateCreated ); $post_date_gmt = iso8601_to_datetime( $dateCreated, 'gmt' ); // Flag the post date to be edited. $edit_date = true; } else { $post_date = $postdata['post_date']; $post_date_gmt = $postdata['post_date_gmt']; } // We've got all the data -- post it. $newpost = compact( 'ID', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt', 'comment_status', 'ping_status', 'edit_date', 'post_date', 'post_date_gmt', 'to_ping', 'post_name', 'post_password', 'post_parent', 'menu_order', 'post_author', 'tags_input', 'page_template' ); $result = wp_update_post( $newpost, true ); if ( is_wp_error( $result ) ) { return new IXR_Error( 500, $result->get_error_message() ); } if ( ! $result ) { return new IXR_Error( 500, __( 'Sorry, the post could not be updated.' ) ); } // Only posts can be sticky. if ( 'post' === $post_type && isset( $content_struct['sticky'] ) ) { $data = $newpost; $data['sticky'] = $content_struct['sticky']; $data['post_type'] = 'post'; $error = $this->_toggle_sticky( $data, true ); if ( $error ) { return $error; } } if ( isset( $content_struct['custom_fields'] ) ) { $this->set_custom_fields( $post_ID, $content_struct['custom_fields'] ); } if ( isset( $content_struct['wp_post_thumbnail'] ) ) { // Empty value deletes, non-empty value adds/updates. if ( empty( $content_struct['wp_post_thumbnail'] ) ) { delete_post_thumbnail( $post_ID ); } else { if ( set_post_thumbnail( $post_ID, $content_struct['wp_post_thumbnail'] ) === false ) { return new IXR_Error( 404, __( 'Invalid attachment ID.' ) ); } } unset( $content_struct['wp_post_thumbnail'] ); } // Handle enclosures. $thisEnclosure = isset( $content_struct['enclosure'] ) ? $content_struct['enclosure'] : null; $this->add_enclosure_if_new( $post_ID, $thisEnclosure ); $this->attach_uploads( $ID, $post_content ); // Handle post formats if assigned, validation is handled earlier in this function. if ( isset( $content_struct['wp_post_format'] ) ) { set_post_format( $post_ID, $content_struct['wp_post_format'] ); } /** * Fires after a post has been successfully updated via the XML-RPC MovableType API. * * @since 3.4.0 * * @param int $post_ID ID of the updated post. * @param array $args An array of arguments to update the post. */ do_action( 'xmlrpc_call_success_mw_editPost', $post_ID, $args ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase return true; } /** * Retrieve post. * * @since 1.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type int $post_ID * @type string $username * @type string $password * } * @return array|IXR_Error */ public function mw_getPost( $args ) { $this->escape( $args ); $post_ID = (int) $args[0]; $username = $args[1]; $password = $args[2]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } $postdata = get_post( $post_ID, ARRAY_A ); if ( ! $postdata ) { return new IXR_Error( 404, __( 'Invalid post ID.' ) ); } if ( ! current_user_can( 'edit_post', $post_ID ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'metaWeblog.getPost', $args, $this ); if ( '' !== $postdata['post_date'] ) { $post_date = $this->_convert_date( $postdata['post_date'] ); $post_date_gmt = $this->_convert_date_gmt( $postdata['post_date_gmt'], $postdata['post_date'] ); $post_modified = $this->_convert_date( $postdata['post_modified'] ); $post_modified_gmt = $this->_convert_date_gmt( $postdata['post_modified_gmt'], $postdata['post_modified'] ); $categories = array(); $catids = wp_get_post_categories( $post_ID ); foreach ( $catids as $catid ) { $categories[] = get_cat_name( $catid ); } $tagnames = array(); $tags = wp_get_post_tags( $post_ID ); if ( ! empty( $tags ) ) { foreach ( $tags as $tag ) { $tagnames[] = $tag->name; } $tagnames = implode( ', ', $tagnames ); } else { $tagnames = ''; } $post = get_extended( $postdata['post_content'] ); $link = get_permalink( $postdata['ID'] ); // Get the author info. $author = get_userdata( $postdata['post_author'] ); $allow_comments = ( 'open' === $postdata['comment_status'] ) ? 1 : 0; $allow_pings = ( 'open' === $postdata['ping_status'] ) ? 1 : 0; // Consider future posts as published. if ( 'future' === $postdata['post_status'] ) { $postdata['post_status'] = 'publish'; } // Get post format. $post_format = get_post_format( $post_ID ); if ( empty( $post_format ) ) { $post_format = 'standard'; } $sticky = false; if ( is_sticky( $post_ID ) ) { $sticky = true; } $enclosure = array(); foreach ( (array) get_post_custom( $post_ID ) as $key => $val ) { if ( 'enclosure' === $key ) { foreach ( (array) $val as $enc ) { $encdata = explode( "\n", $enc ); $enclosure['url'] = trim( htmlspecialchars( $encdata[0] ) ); $enclosure['length'] = (int) trim( $encdata[1] ); $enclosure['type'] = trim( $encdata[2] ); break 2; } } } $resp = array( 'dateCreated' => $post_date, 'userid' => $postdata['post_author'], 'postid' => $postdata['ID'], 'description' => $post['main'], 'title' => $postdata['post_title'], 'link' => $link, 'permaLink' => $link, // Commented out because no other tool seems to use this. // 'content' => $entry['post_content'], 'categories' => $categories, 'mt_excerpt' => $postdata['post_excerpt'], 'mt_text_more' => $post['extended'], 'wp_more_text' => $post['more_text'], 'mt_allow_comments' => $allow_comments, 'mt_allow_pings' => $allow_pings, 'mt_keywords' => $tagnames, 'wp_slug' => $postdata['post_name'], 'wp_password' => $postdata['post_password'], 'wp_author_id' => (string) $author->ID, 'wp_author_display_name' => $author->display_name, 'date_created_gmt' => $post_date_gmt, 'post_status' => $postdata['post_status'], 'custom_fields' => $this->get_custom_fields( $post_ID ), 'wp_post_format' => $post_format, 'sticky' => $sticky, 'date_modified' => $post_modified, 'date_modified_gmt' => $post_modified_gmt, ); if ( ! empty( $enclosure ) ) { $resp['enclosure'] = $enclosure; } $resp['wp_post_thumbnail'] = get_post_thumbnail_id( $postdata['ID'] ); return $resp; } else { return new IXR_Error( 404, __( 'Sorry, no such post.' ) ); } } /** * Retrieve list of recent posts. * * @since 1.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type string $username * @type string $password * @type int $numberposts * } * @return array|IXR_Error */ public function mw_getRecentPosts( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; if ( isset( $args[3] ) ) { $query = array( 'numberposts' => absint( $args[3] ) ); } else { $query = array(); } $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! current_user_can( 'edit_posts' ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit posts.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'metaWeblog.getRecentPosts', $args, $this ); $posts_list = wp_get_recent_posts( $query ); if ( ! $posts_list ) { return array(); } $recent_posts = array(); foreach ( $posts_list as $entry ) { if ( ! current_user_can( 'edit_post', $entry['ID'] ) ) { continue; } $post_date = $this->_convert_date( $entry['post_date'] ); $post_date_gmt = $this->_convert_date_gmt( $entry['post_date_gmt'], $entry['post_date'] ); $post_modified = $this->_convert_date( $entry['post_modified'] ); $post_modified_gmt = $this->_convert_date_gmt( $entry['post_modified_gmt'], $entry['post_modified'] ); $categories = array(); $catids = wp_get_post_categories( $entry['ID'] ); foreach ( $catids as $catid ) { $categories[] = get_cat_name( $catid ); } $tagnames = array(); $tags = wp_get_post_tags( $entry['ID'] ); if ( ! empty( $tags ) ) { foreach ( $tags as $tag ) { $tagnames[] = $tag->name; } $tagnames = implode( ', ', $tagnames ); } else { $tagnames = ''; } $post = get_extended( $entry['post_content'] ); $link = get_permalink( $entry['ID'] ); // Get the post author info. $author = get_userdata( $entry['post_author'] ); $allow_comments = ( 'open' === $entry['comment_status'] ) ? 1 : 0; $allow_pings = ( 'open' === $entry['ping_status'] ) ? 1 : 0; // Consider future posts as published. if ( 'future' === $entry['post_status'] ) { $entry['post_status'] = 'publish'; } // Get post format. $post_format = get_post_format( $entry['ID'] ); if ( empty( $post_format ) ) { $post_format = 'standard'; } $recent_posts[] = array( 'dateCreated' => $post_date, 'userid' => $entry['post_author'], 'postid' => (string) $entry['ID'], 'description' => $post['main'], 'title' => $entry['post_title'], 'link' => $link, 'permaLink' => $link, // Commented out because no other tool seems to use this. // 'content' => $entry['post_content'], 'categories' => $categories, 'mt_excerpt' => $entry['post_excerpt'], 'mt_text_more' => $post['extended'], 'wp_more_text' => $post['more_text'], 'mt_allow_comments' => $allow_comments, 'mt_allow_pings' => $allow_pings, 'mt_keywords' => $tagnames, 'wp_slug' => $entry['post_name'], 'wp_password' => $entry['post_password'], 'wp_author_id' => (string) $author->ID, 'wp_author_display_name' => $author->display_name, 'date_created_gmt' => $post_date_gmt, 'post_status' => $entry['post_status'], 'custom_fields' => $this->get_custom_fields( $entry['ID'] ), 'wp_post_format' => $post_format, 'date_modified' => $post_modified, 'date_modified_gmt' => $post_modified_gmt, 'sticky' => ( 'post' === $entry['post_type'] && is_sticky( $entry['ID'] ) ), 'wp_post_thumbnail' => get_post_thumbnail_id( $entry['ID'] ), ); } return $recent_posts; } /** * Retrieve the list of categories on a given blog. * * @since 1.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type string $username * @type string $password * } * @return array|IXR_Error */ public function mw_getCategories( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! current_user_can( 'edit_posts' ) ) { return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view categories.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'metaWeblog.getCategories', $args, $this ); $categories_struct = array(); $cats = get_categories( array( 'get' => 'all' ) ); if ( $cats ) { foreach ( $cats as $cat ) { $struct = array(); $struct['categoryId'] = $cat->term_id; $struct['parentId'] = $cat->parent; $struct['description'] = $cat->name; $struct['categoryDescription'] = $cat->description; $struct['categoryName'] = $cat->name; $struct['htmlUrl'] = esc_html( get_category_link( $cat->term_id ) ); $struct['rssUrl'] = esc_html( get_category_feed_link( $cat->term_id, 'rss2' ) ); $categories_struct[] = $struct; } } return $categories_struct; } /** * Uploads a file, following your settings. * * Adapted from a patch by Johann Richard. * * @link http://mycvs.org/archives/2004/06/30/file-upload-to-wordpress-in-ecto/ * * @since 1.5.0 * * @global wpdb $wpdb WordPress database abstraction object. * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type string $username * @type string $password * @type array $data * } * @return array|IXR_Error */ public function mw_newMediaObject( $args ) { global $wpdb; $username = $this->escape( $args[1] ); $password = $this->escape( $args[2] ); $data = $args[3]; $name = sanitize_file_name( $data['name'] ); $type = $data['type']; $bits = $data['bits']; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'metaWeblog.newMediaObject', $args, $this ); if ( ! current_user_can( 'upload_files' ) ) { $this->error = new IXR_Error( 401, __( 'Sorry, you are not allowed to upload files.' ) ); return $this->error; } if ( is_multisite() && upload_is_user_over_quota( false ) ) { $this->error = new IXR_Error( 401, sprintf( /* translators: %s: Allowed space allocation. */ __( 'Sorry, you have used your space allocation of %s. Please delete some files to upload more files.' ), size_format( get_space_allowed() * MB_IN_BYTES ) ) ); return $this->error; } /** * Filters whether to preempt the XML-RPC media upload. * * Passing a truthy value will effectively short-circuit the media upload, * returning that value as a 500 error instead. * * @since 2.1.0 * * @param bool $error Whether to pre-empt the media upload. Default false. */ $upload_err = apply_filters( 'pre_upload_error', false ); if ( $upload_err ) { return new IXR_Error( 500, $upload_err ); } $upload = wp_upload_bits( $name, null, $bits ); if ( ! empty( $upload['error'] ) ) { /* translators: 1: File name, 2: Error message. */ $errorString = sprintf( __( 'Could not write file %1$s (%2$s).' ), $name, $upload['error'] ); return new IXR_Error( 500, $errorString ); } // Construct the attachment array. $post_id = 0; if ( ! empty( $data['post_id'] ) ) { $post_id = (int) $data['post_id']; if ( ! current_user_can( 'edit_post', $post_id ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) ); } } $attachment = array( 'post_title' => $name, 'post_content' => '', 'post_type' => 'attachment', 'post_parent' => $post_id, 'post_mime_type' => $type, 'guid' => $upload['url'], ); // Save the data. $id = wp_insert_attachment( $attachment, $upload['file'], $post_id ); wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $upload['file'] ) ); /** * Fires after a new attachment has been added via the XML-RPC MovableType API. * * @since 3.4.0 * * @param int $id ID of the new attachment. * @param array $args An array of arguments to add the attachment. */ do_action( 'xmlrpc_call_success_mw_newMediaObject', $id, $args ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase $struct = $this->_prepare_media_item( get_post( $id ) ); // Deprecated values. $struct['id'] = $struct['attachment_id']; $struct['file'] = $struct['title']; $struct['url'] = $struct['link']; return $struct; } /* * MovableType API functions. * Specs on http://www.movabletype.org/docs/mtmanual_programmatic.html */ /** * Retrieve the post titles of recent posts. * * @since 1.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type string $username * @type string $password * @type int $numberposts * } * @return array|IXR_Error */ public function mt_getRecentPostTitles( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; if ( isset( $args[3] ) ) { $query = array( 'numberposts' => absint( $args[3] ) ); } else { $query = array(); } $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'mt.getRecentPostTitles', $args, $this ); $posts_list = wp_get_recent_posts( $query ); if ( ! $posts_list ) { $this->error = new IXR_Error( 500, __( 'Either there are no posts, or something went wrong.' ) ); return $this->error; } $recent_posts = array(); foreach ( $posts_list as $entry ) { if ( ! current_user_can( 'edit_post', $entry['ID'] ) ) { continue; } $post_date = $this->_convert_date( $entry['post_date'] ); $post_date_gmt = $this->_convert_date_gmt( $entry['post_date_gmt'], $entry['post_date'] ); $recent_posts[] = array( 'dateCreated' => $post_date, 'userid' => $entry['post_author'], 'postid' => (string) $entry['ID'], 'title' => $entry['post_title'], 'post_status' => $entry['post_status'], 'date_created_gmt' => $post_date_gmt, ); } return $recent_posts; } /** * Retrieve list of all categories on blog. * * @since 1.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $blog_id (unused) * @type string $username * @type string $password * } * @return array|IXR_Error */ public function mt_getCategoryList( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! current_user_can( 'edit_posts' ) ) { return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view categories.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'mt.getCategoryList', $args, $this ); $categories_struct = array(); $cats = get_categories( array( 'hide_empty' => 0, 'hierarchical' => 0, ) ); if ( $cats ) { foreach ( $cats as $cat ) { $struct = array(); $struct['categoryId'] = $cat->term_id; $struct['categoryName'] = $cat->name; $categories_struct[] = $struct; } } return $categories_struct; } /** * Retrieve post categories. * * @since 1.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $post_ID * @type string $username * @type string $password * } * @return array|IXR_Error */ public function mt_getPostCategories( $args ) { $this->escape( $args ); $post_ID = (int) $args[0]; $username = $args[1]; $password = $args[2]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! get_post( $post_ID ) ) { return new IXR_Error( 404, __( 'Invalid post ID.' ) ); } if ( ! current_user_can( 'edit_post', $post_ID ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'mt.getPostCategories', $args, $this ); $categories = array(); $catids = wp_get_post_categories( (int) $post_ID ); // First listed category will be the primary category. $isPrimary = true; foreach ( $catids as $catid ) { $categories[] = array( 'categoryName' => get_cat_name( $catid ), 'categoryId' => (string) $catid, 'isPrimary' => $isPrimary, ); $isPrimary = false; } return $categories; } /** * Sets categories for a post. * * @since 1.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $post_ID * @type string $username * @type string $password * @type array $categories * } * @return true|IXR_Error True on success. */ public function mt_setPostCategories( $args ) { $this->escape( $args ); $post_ID = (int) $args[0]; $username = $args[1]; $password = $args[2]; $categories = $args[3]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'mt.setPostCategories', $args, $this ); if ( ! get_post( $post_ID ) ) { return new IXR_Error( 404, __( 'Invalid post ID.' ) ); } if ( ! current_user_can( 'edit_post', $post_ID ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) ); } $catids = array(); foreach ( $categories as $cat ) { $catids[] = $cat['categoryId']; } wp_set_post_categories( $post_ID, $catids ); return true; } /** * Retrieve an array of methods supported by this server. * * @since 1.5.0 * * @return array */ public function mt_supportedMethods() { /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'mt.supportedMethods', array(), $this ); return array_keys( $this->methods ); } /** * Retrieve an empty array because we don't support per-post text filters. * * @since 1.5.0 */ public function mt_supportedTextFilters() { /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'mt.supportedTextFilters', array(), $this ); /** * Filters the MoveableType text filters list for XML-RPC. * * @since 2.2.0 * * @param array $filters An array of text filters. */ return apply_filters( 'xmlrpc_text_filters', array() ); } /** * Retrieve trackbacks sent to a given post. * * @since 1.5.0 * * @global wpdb $wpdb WordPress database abstraction object. * * @param int $post_ID * @return array|IXR_Error */ public function mt_getTrackbackPings( $post_ID ) { global $wpdb; /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'mt.getTrackbackPings', $post_ID, $this ); $actual_post = get_post( $post_ID, ARRAY_A ); if ( ! $actual_post ) { return new IXR_Error( 404, __( 'Sorry, no such post.' ) ); } $comments = $wpdb->get_results( $wpdb->prepare( "SELECT comment_author_url, comment_content, comment_author_IP, comment_type FROM $wpdb->comments WHERE comment_post_ID = %d", $post_ID ) ); if ( ! $comments ) { return array(); } $trackback_pings = array(); foreach ( $comments as $comment ) { if ( 'trackback' === $comment->comment_type ) { $content = $comment->comment_content; $title = substr( $content, 8, ( strpos( $content, '</strong>' ) - 8 ) ); $trackback_pings[] = array( 'pingTitle' => $title, 'pingURL' => $comment->comment_author_url, 'pingIP' => $comment->comment_author_IP, ); } } return $trackback_pings; } /** * Sets a post's publish status to 'publish'. * * @since 1.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $post_ID * @type string $username * @type string $password * } * @return int|IXR_Error */ public function mt_publishPost( $args ) { $this->escape( $args ); $post_ID = (int) $args[0]; $username = $args[1]; $password = $args[2]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'mt.publishPost', $args, $this ); $postdata = get_post( $post_ID, ARRAY_A ); if ( ! $postdata ) { return new IXR_Error( 404, __( 'Invalid post ID.' ) ); } if ( ! current_user_can( 'publish_posts' ) || ! current_user_can( 'edit_post', $post_ID ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to publish this post.' ) ); } $postdata['post_status'] = 'publish'; // Retain old categories. $postdata['post_category'] = wp_get_post_categories( $post_ID ); $this->escape( $postdata ); return wp_update_post( $postdata ); } /* * Pingback functions. * Specs on www.hixie.ch/specs/pingback/pingback */ /** * Retrieves a pingback and registers it. * * @since 1.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type string $pagelinkedfrom * @type string $pagelinkedto * } * @return string|IXR_Error */ public function pingback_ping( $args ) { global $wpdb; /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'pingback.ping', $args, $this ); $this->escape( $args ); $pagelinkedfrom = str_replace( '&', '&', $args[0] ); $pagelinkedto = str_replace( '&', '&', $args[1] ); $pagelinkedto = str_replace( '&', '&', $pagelinkedto ); /** * Filters the pingback source URI. * * @since 3.6.0 * * @param string $pagelinkedfrom URI of the page linked from. * @param string $pagelinkedto URI of the page linked to. */ $pagelinkedfrom = apply_filters( 'pingback_ping_source_uri', $pagelinkedfrom, $pagelinkedto ); if ( ! $pagelinkedfrom ) { return $this->pingback_error( 0, __( 'A valid URL was not provided.' ) ); } // Check if the page linked to is on our site. $pos1 = strpos( $pagelinkedto, str_replace( array( 'http://www.', 'http://', 'https://www.', 'https://' ), '', get_option( 'home' ) ) ); if ( ! $pos1 ) { return $this->pingback_error( 0, __( 'Is there no link to us?' ) ); } /* * Let's find which post is linked to. * FIXME: Does url_to_postid() cover all these cases already? * If so, then let's use it and drop the old code. */ $urltest = parse_url( $pagelinkedto ); $post_ID = url_to_postid( $pagelinkedto ); if ( $post_ID ) { // $way } elseif ( isset( $urltest['path'] ) && preg_match( '#p/[0-9]{1,}#', $urltest['path'], $match ) ) { // The path defines the post_ID (archives/p/XXXX). $blah = explode( '/', $match[0] ); $post_ID = (int) $blah[1]; } elseif ( isset( $urltest['query'] ) && preg_match( '#p=[0-9]{1,}#', $urltest['query'], $match ) ) { // The query string defines the post_ID (?p=XXXX). $blah = explode( '=', $match[0] ); $post_ID = (int) $blah[1]; } elseif ( isset( $urltest['fragment'] ) ) { // An #anchor is there, it's either... if ( (int) $urltest['fragment'] ) { // ...an integer #XXXX (simplest case), $post_ID = (int) $urltest['fragment']; } elseif ( preg_match( '/post-[0-9]+/', $urltest['fragment'] ) ) { // ...a post ID in the form 'post-###', $post_ID = preg_replace( '/[^0-9]+/', '', $urltest['fragment'] ); } elseif ( is_string( $urltest['fragment'] ) ) { // ...or a string #title, a little more complicated. $title = preg_replace( '/[^a-z0-9]/i', '.', $urltest['fragment'] ); $sql = $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_title RLIKE %s", $title ); $post_ID = $wpdb->get_var( $sql ); if ( ! $post_ID ) { // Returning unknown error '0' is better than die()'ing. return $this->pingback_error( 0, '' ); } } } else { // TODO: Attempt to extract a post ID from the given URL. return $this->pingback_error( 33, __( 'The specified target URL cannot be used as a target. It either doesn’t exist, or it is not a pingback-enabled resource.' ) ); } $post_ID = (int) $post_ID; $post = get_post( $post_ID ); if ( ! $post ) { // Post not found. return $this->pingback_error( 33, __( 'The specified target URL cannot be used as a target. It either doesn’t exist, or it is not a pingback-enabled resource.' ) ); } if ( url_to_postid( $pagelinkedfrom ) == $post_ID ) { return $this->pingback_error( 0, __( 'The source URL and the target URL cannot both point to the same resource.' ) ); } // Check if pings are on. if ( ! pings_open( $post ) ) { return $this->pingback_error( 33, __( 'The specified target URL cannot be used as a target. It either doesn’t exist, or it is not a pingback-enabled resource.' ) ); } // Let's check that the remote site didn't already pingback this entry. if ( $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_author_url = %s", $post_ID, $pagelinkedfrom ) ) ) { return $this->pingback_error( 48, __( 'The pingback has already been registered.' ) ); } // Very stupid, but gives time to the 'from' server to publish! sleep( 1 ); $remote_ip = preg_replace( '/[^0-9a-fA-F:., ]/', '', $_SERVER['REMOTE_ADDR'] ); /** This filter is documented in wp-includes/class-http.php */ $user_agent = apply_filters( 'http_headers_useragent', 'WordPress/' . get_bloginfo( 'version' ) . '; ' . get_bloginfo( 'url' ), $pagelinkedfrom ); // Let's check the remote site. $http_api_args = array( 'timeout' => 10, 'redirection' => 0, 'limit_response_size' => 153600, // 150 KB 'user-agent' => "$user_agent; verifying pingback from $remote_ip", 'headers' => array( 'X-Pingback-Forwarded-For' => $remote_ip, ), ); $request = wp_safe_remote_get( $pagelinkedfrom, $http_api_args ); $remote_source = wp_remote_retrieve_body( $request ); $remote_source_original = $remote_source; if ( ! $remote_source ) { return $this->pingback_error( 16, __( 'The source URL does not exist.' ) ); } /** * Filters the pingback remote source. * * @since 2.5.0 * * @param string $remote_source Response source for the page linked from. * @param string $pagelinkedto URL of the page linked to. */ $remote_source = apply_filters( 'pre_remote_source', $remote_source, $pagelinkedto ); // Work around bug in strip_tags(): $remote_source = str_replace( '<!DOC', '<DOC', $remote_source ); $remote_source = preg_replace( '/[\r\n\t ]+/', ' ', $remote_source ); // normalize spaces $remote_source = preg_replace( '/<\/*(h1|h2|h3|h4|h5|h6|p|th|td|li|dt|dd|pre|caption|input|textarea|button|body)[^>]*>/', "\n\n", $remote_source ); preg_match( '|<title>([^<]*?)</title>|is', $remote_source, $matchtitle ); $title = isset( $matchtitle[1] ) ? $matchtitle[1] : ''; if ( empty( $title ) ) { return $this->pingback_error( 32, __( 'We cannot find a title on that page.' ) ); } // Remove all script and style tags including their content. $remote_source = preg_replace( '@<(script|style)[^>]*?>.*?</\\1>@si', '', $remote_source ); // Just keep the tag we need. $remote_source = strip_tags( $remote_source, '<a>' ); $p = explode( "\n\n", $remote_source ); $preg_target = preg_quote( $pagelinkedto, '|' ); foreach ( $p as $para ) { if ( strpos( $para, $pagelinkedto ) !== false ) { // It exists, but is it a link? preg_match( '|<a[^>]+?' . $preg_target . '[^>]*>([^>]+?)</a>|', $para, $context ); // If the URL isn't in a link context, keep looking. if ( empty( $context ) ) { continue; } // We're going to use this fake tag to mark the context in a bit. // The marker is needed in case the link text appears more than once in the paragraph. $excerpt = preg_replace( '|\</?wpcontext\>|', '', $para ); // prevent really long link text if ( strlen( $context[1] ) > 100 ) { $context[1] = substr( $context[1], 0, 100 ) . '…'; } $marker = '<wpcontext>' . $context[1] . '</wpcontext>'; // Set up our marker. $excerpt = str_replace( $context[0], $marker, $excerpt ); // Swap out the link for our marker. $excerpt = strip_tags( $excerpt, '<wpcontext>' ); // Strip all tags but our context marker. $excerpt = trim( $excerpt ); $preg_marker = preg_quote( $marker, '|' ); $excerpt = preg_replace( "|.*?\s(.{0,100}$preg_marker.{0,100})\s.*|s", '$1', $excerpt ); $excerpt = strip_tags( $excerpt ); // YES, again, to remove the marker wrapper. break; } } if ( empty( $context ) ) { // Link to target not found. return $this->pingback_error( 17, __( 'The source URL does not contain a link to the target URL, and so cannot be used as a source.' ) ); } $pagelinkedfrom = str_replace( '&', '&', $pagelinkedfrom ); $context = '[…] ' . esc_html( $excerpt ) . ' […]'; $pagelinkedfrom = $this->escape( $pagelinkedfrom ); $comment_post_ID = (int) $post_ID; $comment_author = $title; $comment_author_email = ''; $this->escape( $comment_author ); $comment_author_url = $pagelinkedfrom; $comment_content = $context; $this->escape( $comment_content ); $comment_type = 'pingback'; $commentdata = compact( 'comment_post_ID', 'comment_author', 'comment_author_url', 'comment_author_email', 'comment_content', 'comment_type', 'remote_source', 'remote_source_original' ); $comment_ID = wp_new_comment( $commentdata ); if ( is_wp_error( $comment_ID ) ) { return $this->pingback_error( 0, $comment_ID->get_error_message() ); } /** * Fires after a post pingback has been sent. * * @since 0.71 * * @param int $comment_ID Comment ID. */ do_action( 'pingback_post', $comment_ID ); /* translators: 1: URL of the page linked from, 2: URL of the page linked to. */ return sprintf( __( 'Pingback from %1$s to %2$s registered. Keep the web talking! :-)' ), $pagelinkedfrom, $pagelinkedto ); } /** * Retrieve array of URLs that pingbacked the given URL. * * Specs on http://www.aquarionics.com/misc/archives/blogite/0198.html * * @since 1.5.0 * * @global wpdb $wpdb WordPress database abstraction object. * * @param string $url * @return array|IXR_Error */ public function pingback_extensions_getPingbacks( $url ) { global $wpdb; /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'pingback.extensions.getPingbacks', $url, $this ); $url = $this->escape( $url ); $post_ID = url_to_postid( $url ); if ( ! $post_ID ) { // We aren't sure that the resource is available and/or pingback enabled. return $this->pingback_error( 33, __( 'The specified target URL cannot be used as a target. It either doesn’t exist, or it is not a pingback-enabled resource.' ) ); } $actual_post = get_post( $post_ID, ARRAY_A ); if ( ! $actual_post ) { // No such post = resource not found. return $this->pingback_error( 32, __( 'The specified target URL does not exist.' ) ); } $comments = $wpdb->get_results( $wpdb->prepare( "SELECT comment_author_url, comment_content, comment_author_IP, comment_type FROM $wpdb->comments WHERE comment_post_ID = %d", $post_ID ) ); if ( ! $comments ) { return array(); } $pingbacks = array(); foreach ( $comments as $comment ) { if ( 'pingback' === $comment->comment_type ) { $pingbacks[] = $comment->comment_author_url; } } return $pingbacks; } /** * Sends a pingback error based on the given error code and message. * * @since 3.6.0 * * @param int $code Error code. * @param string $message Error message. * @return IXR_Error Error object. */ protected function pingback_error( $code, $message ) { /** * Filters the XML-RPC pingback error return. * * @since 3.5.1 * * @param IXR_Error $error An IXR_Error object containing the error code and message. */ return apply_filters( 'xmlrpc_pingback_error', new IXR_Error( $code, $message ) ); } } home/verseaumee/empowernetorg/wp-includes/class-wp-xmlrpc-server.php 0000604 00000643644 15075304774 0022066 0 ustar 00 <?php /** * XML-RPC protocol support for WordPress. * * @package WordPress * @subpackage Publishing */ /** * WordPress XMLRPC server implementation. * * Implements compatibility for Blogger API, MetaWeblog API, MovableType, and * pingback. Additional WordPress API for managing comments, pages, posts, * options, etc. * * As of WordPress 3.5.0, XML-RPC is enabled by default. It can be disabled * via the {@see 'xmlrpc_enabled'} filter found in wp_xmlrpc_server::set_is_enabled(). * * @since 1.5.0 * * @see IXR_Server */ #[AllowDynamicProperties] class wp_xmlrpc_server extends IXR_Server { /** * Methods. * * @var array */ public $methods; /** * Blog options. * * @var array */ public $blog_options; /** * IXR_Error instance. * * @var IXR_Error */ public $error; /** * Flags that the user authentication has failed in this instance of wp_xmlrpc_server. * * @var bool */ protected $auth_failed = false; /** * Flags that XML-RPC is enabled * * @var bool */ private $is_enabled; /** * Registers all of the XMLRPC methods that XMLRPC server understands. * * Sets up server and method property. Passes XMLRPC methods through the * {@see 'xmlrpc_methods'} filter to allow plugins to extend or replace * XML-RPC methods. * * @since 1.5.0 */ public function __construct() { $this->methods = array( // WordPress API. 'wp.getUsersBlogs' => 'this:wp_getUsersBlogs', 'wp.newPost' => 'this:wp_newPost', 'wp.editPost' => 'this:wp_editPost', 'wp.deletePost' => 'this:wp_deletePost', 'wp.getPost' => 'this:wp_getPost', 'wp.getPosts' => 'this:wp_getPosts', 'wp.newTerm' => 'this:wp_newTerm', 'wp.editTerm' => 'this:wp_editTerm', 'wp.deleteTerm' => 'this:wp_deleteTerm', 'wp.getTerm' => 'this:wp_getTerm', 'wp.getTerms' => 'this:wp_getTerms', 'wp.getTaxonomy' => 'this:wp_getTaxonomy', 'wp.getTaxonomies' => 'this:wp_getTaxonomies', 'wp.getUser' => 'this:wp_getUser', 'wp.getUsers' => 'this:wp_getUsers', 'wp.getProfile' => 'this:wp_getProfile', 'wp.editProfile' => 'this:wp_editProfile', 'wp.getPage' => 'this:wp_getPage', 'wp.getPages' => 'this:wp_getPages', 'wp.newPage' => 'this:wp_newPage', 'wp.deletePage' => 'this:wp_deletePage', 'wp.editPage' => 'this:wp_editPage', 'wp.getPageList' => 'this:wp_getPageList', 'wp.getAuthors' => 'this:wp_getAuthors', 'wp.getCategories' => 'this:mw_getCategories', // Alias. 'wp.getTags' => 'this:wp_getTags', 'wp.newCategory' => 'this:wp_newCategory', 'wp.deleteCategory' => 'this:wp_deleteCategory', 'wp.suggestCategories' => 'this:wp_suggestCategories', 'wp.uploadFile' => 'this:mw_newMediaObject', // Alias. 'wp.deleteFile' => 'this:wp_deletePost', // Alias. 'wp.getCommentCount' => 'this:wp_getCommentCount', 'wp.getPostStatusList' => 'this:wp_getPostStatusList', 'wp.getPageStatusList' => 'this:wp_getPageStatusList', 'wp.getPageTemplates' => 'this:wp_getPageTemplates', 'wp.getOptions' => 'this:wp_getOptions', 'wp.setOptions' => 'this:wp_setOptions', 'wp.getComment' => 'this:wp_getComment', 'wp.getComments' => 'this:wp_getComments', 'wp.deleteComment' => 'this:wp_deleteComment', 'wp.editComment' => 'this:wp_editComment', 'wp.newComment' => 'this:wp_newComment', 'wp.getCommentStatusList' => 'this:wp_getCommentStatusList', 'wp.getMediaItem' => 'this:wp_getMediaItem', 'wp.getMediaLibrary' => 'this:wp_getMediaLibrary', 'wp.getPostFormats' => 'this:wp_getPostFormats', 'wp.getPostType' => 'this:wp_getPostType', 'wp.getPostTypes' => 'this:wp_getPostTypes', 'wp.getRevisions' => 'this:wp_getRevisions', 'wp.restoreRevision' => 'this:wp_restoreRevision', // Blogger API. 'blogger.getUsersBlogs' => 'this:blogger_getUsersBlogs', 'blogger.getUserInfo' => 'this:blogger_getUserInfo', 'blogger.getPost' => 'this:blogger_getPost', 'blogger.getRecentPosts' => 'this:blogger_getRecentPosts', 'blogger.newPost' => 'this:blogger_newPost', 'blogger.editPost' => 'this:blogger_editPost', 'blogger.deletePost' => 'this:blogger_deletePost', // MetaWeblog API (with MT extensions to structs). 'metaWeblog.newPost' => 'this:mw_newPost', 'metaWeblog.editPost' => 'this:mw_editPost', 'metaWeblog.getPost' => 'this:mw_getPost', 'metaWeblog.getRecentPosts' => 'this:mw_getRecentPosts', 'metaWeblog.getCategories' => 'this:mw_getCategories', 'metaWeblog.newMediaObject' => 'this:mw_newMediaObject', /* * MetaWeblog API aliases for Blogger API. * See http://www.xmlrpc.com/stories/storyReader$2460 */ 'metaWeblog.deletePost' => 'this:blogger_deletePost', 'metaWeblog.getUsersBlogs' => 'this:blogger_getUsersBlogs', // MovableType API. 'mt.getCategoryList' => 'this:mt_getCategoryList', 'mt.getRecentPostTitles' => 'this:mt_getRecentPostTitles', 'mt.getPostCategories' => 'this:mt_getPostCategories', 'mt.setPostCategories' => 'this:mt_setPostCategories', 'mt.supportedMethods' => 'this:mt_supportedMethods', 'mt.supportedTextFilters' => 'this:mt_supportedTextFilters', 'mt.getTrackbackPings' => 'this:mt_getTrackbackPings', 'mt.publishPost' => 'this:mt_publishPost', // Pingback. 'pingback.ping' => 'this:pingback_ping', 'pingback.extensions.getPingbacks' => 'this:pingback_extensions_getPingbacks', 'demo.sayHello' => 'this:sayHello', 'demo.addTwoNumbers' => 'this:addTwoNumbers', ); $this->initialise_blog_option_info(); /** * Filters the methods exposed by the XML-RPC server. * * This filter can be used to add new methods, and remove built-in methods. * * @since 1.5.0 * * @param string[] $methods An array of XML-RPC methods, keyed by their methodName. */ $this->methods = apply_filters( 'xmlrpc_methods', $this->methods ); $this->set_is_enabled(); } /** * Sets wp_xmlrpc_server::$is_enabled property. * * Determines whether the xmlrpc server is enabled on this WordPress install * and set the is_enabled property accordingly. * * @since 5.7.3 */ private function set_is_enabled() { /* * Respect old get_option() filters left for back-compat when the 'enable_xmlrpc' * option was deprecated in 3.5.0. Use the {@see 'xmlrpc_enabled'} hook instead. */ $is_enabled = apply_filters( 'pre_option_enable_xmlrpc', false ); if ( false === $is_enabled ) { $is_enabled = apply_filters( 'option_enable_xmlrpc', true ); } /** * Filters whether XML-RPC methods requiring authentication are enabled. * * Contrary to the way it's named, this filter does not control whether XML-RPC is *fully* * enabled, rather, it only controls whether XML-RPC methods requiring authentication - * such as for publishing purposes - are enabled. * * Further, the filter does not control whether pingbacks or other custom endpoints that don't * require authentication are enabled. This behavior is expected, and due to how parity was matched * with the `enable_xmlrpc` UI option the filter replaced when it was introduced in 3.5. * * To disable XML-RPC methods that require authentication, use: * * add_filter( 'xmlrpc_enabled', '__return_false' ); * * For more granular control over all XML-RPC methods and requests, see the {@see 'xmlrpc_methods'} * and {@see 'xmlrpc_element_limit'} hooks. * * @since 3.5.0 * * @param bool $is_enabled Whether XML-RPC is enabled. Default true. */ $this->is_enabled = apply_filters( 'xmlrpc_enabled', $is_enabled ); } /** * Makes private/protected methods readable for backward compatibility. * * @since 4.0.0 * * @param string $name Method to call. * @param array $arguments Arguments to pass when calling. * @return array|IXR_Error|false Return value of the callback, false otherwise. */ public function __call( $name, $arguments ) { if ( '_multisite_getUsersBlogs' === $name ) { return $this->_multisite_getUsersBlogs( ...$arguments ); } return false; } /** * Serves the XML-RPC request. * * @since 2.9.0 */ public function serve_request() { $this->IXR_Server( $this->methods ); } /** * Tests XMLRPC API by saying, "Hello!" to client. * * @since 1.5.0 * * @return string Hello string response. */ public function sayHello() { return 'Hello!'; } /** * Tests XMLRPC API by adding two numbers for client. * * @since 1.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 A number to add. * @type int $1 A second number to add. * } * @return int Sum of the two given numbers. */ public function addTwoNumbers( $args ) { $number1 = $args[0]; $number2 = $args[1]; return $number1 + $number2; } /** * Logs user in. * * @since 2.8.0 * * @param string $username User's username. * @param string $password User's password. * @return WP_User|false WP_User object if authentication passed, false otherwise. */ public function login( $username, $password ) { if ( ! $this->is_enabled ) { $this->error = new IXR_Error( 405, sprintf( __( 'XML-RPC services are disabled on this site.' ) ) ); return false; } if ( $this->auth_failed ) { $user = new WP_Error( 'login_prevented' ); } else { $user = wp_authenticate( $username, $password ); } if ( is_wp_error( $user ) ) { $this->error = new IXR_Error( 403, __( 'Incorrect username or password.' ) ); // Flag that authentication has failed once on this wp_xmlrpc_server instance. $this->auth_failed = true; /** * Filters the XML-RPC user login error message. * * @since 3.5.0 * * @param IXR_Error $error The XML-RPC error message. * @param WP_Error $user WP_Error object. */ $this->error = apply_filters( 'xmlrpc_login_error', $this->error, $user ); return false; } wp_set_current_user( $user->ID ); return $user; } /** * Checks user's credentials. Deprecated. * * @since 1.5.0 * @deprecated 2.8.0 Use wp_xmlrpc_server::login() * @see wp_xmlrpc_server::login() * * @param string $username User's username. * @param string $password User's password. * @return bool Whether authentication passed. */ public function login_pass_ok( $username, $password ) { return (bool) $this->login( $username, $password ); } /** * Escapes string or array of strings for database. * * @since 1.5.2 * * @param string|array $data Escape single string or array of strings. * @return string|void Returns with string is passed, alters by-reference * when array is passed. */ public function escape( &$data ) { if ( ! is_array( $data ) ) { return wp_slash( $data ); } foreach ( $data as &$v ) { if ( is_array( $v ) ) { $this->escape( $v ); } elseif ( ! is_object( $v ) ) { $v = wp_slash( $v ); } } } /** * Sends error response to client. * * Sends an XML error response to the client. If the endpoint is enabled * an HTTP 200 response is always sent per the XML-RPC specification. * * @since 5.7.3 * * @param IXR_Error|string $error Error code or an error object. * @param false $message Error message. Optional. */ public function error( $error, $message = false ) { // Accepts either an error object or an error code and message if ( $message && ! is_object( $error ) ) { $error = new IXR_Error( $error, $message ); } if ( ! $this->is_enabled ) { status_header( $error->code ); } $this->output( $error->getXml() ); } /** * Retrieves custom fields for post. * * @since 2.5.0 * * @param int $post_id Post ID. * @return array Custom fields, if exist. */ public function get_custom_fields( $post_id ) { $post_id = (int) $post_id; $custom_fields = array(); foreach ( (array) has_meta( $post_id ) as $meta ) { // Don't expose protected fields. if ( ! current_user_can( 'edit_post_meta', $post_id, $meta['meta_key'] ) ) { continue; } $custom_fields[] = array( 'id' => $meta['meta_id'], 'key' => $meta['meta_key'], 'value' => $meta['meta_value'], ); } return $custom_fields; } /** * Sets custom fields for post. * * @since 2.5.0 * * @param int $post_id Post ID. * @param array $fields Custom fields. */ public function set_custom_fields( $post_id, $fields ) { $post_id = (int) $post_id; foreach ( (array) $fields as $meta ) { if ( isset( $meta['id'] ) ) { $meta['id'] = (int) $meta['id']; $pmeta = get_metadata_by_mid( 'post', $meta['id'] ); if ( ! $pmeta || $pmeta->post_id != $post_id ) { continue; } if ( isset( $meta['key'] ) ) { $meta['key'] = wp_unslash( $meta['key'] ); if ( $meta['key'] !== $pmeta->meta_key ) { continue; } $meta['value'] = wp_unslash( $meta['value'] ); if ( current_user_can( 'edit_post_meta', $post_id, $meta['key'] ) ) { update_metadata_by_mid( 'post', $meta['id'], $meta['value'] ); } } elseif ( current_user_can( 'delete_post_meta', $post_id, $pmeta->meta_key ) ) { delete_metadata_by_mid( 'post', $meta['id'] ); } } elseif ( current_user_can( 'add_post_meta', $post_id, wp_unslash( $meta['key'] ) ) ) { add_post_meta( $post_id, $meta['key'], $meta['value'] ); } } } /** * Retrieves custom fields for a term. * * @since 4.9.0 * * @param int $term_id Term ID. * @return array Array of custom fields, if they exist. */ public function get_term_custom_fields( $term_id ) { $term_id = (int) $term_id; $custom_fields = array(); foreach ( (array) has_term_meta( $term_id ) as $meta ) { if ( ! current_user_can( 'edit_term_meta', $term_id ) ) { continue; } $custom_fields[] = array( 'id' => $meta['meta_id'], 'key' => $meta['meta_key'], 'value' => $meta['meta_value'], ); } return $custom_fields; } /** * Sets custom fields for a term. * * @since 4.9.0 * * @param int $term_id Term ID. * @param array $fields Custom fields. */ public function set_term_custom_fields( $term_id, $fields ) { $term_id = (int) $term_id; foreach ( (array) $fields as $meta ) { if ( isset( $meta['id'] ) ) { $meta['id'] = (int) $meta['id']; $pmeta = get_metadata_by_mid( 'term', $meta['id'] ); if ( isset( $meta['key'] ) ) { $meta['key'] = wp_unslash( $meta['key'] ); if ( $meta['key'] !== $pmeta->meta_key ) { continue; } $meta['value'] = wp_unslash( $meta['value'] ); if ( current_user_can( 'edit_term_meta', $term_id ) ) { update_metadata_by_mid( 'term', $meta['id'], $meta['value'] ); } } elseif ( current_user_can( 'delete_term_meta', $term_id ) ) { delete_metadata_by_mid( 'term', $meta['id'] ); } } elseif ( current_user_can( 'add_term_meta', $term_id ) ) { add_term_meta( $term_id, $meta['key'], $meta['value'] ); } } } /** * Sets up blog options property. * * Passes property through {@see 'xmlrpc_blog_options'} filter. * * @since 2.6.0 */ public function initialise_blog_option_info() { $this->blog_options = array( // Read-only options. 'software_name' => array( 'desc' => __( 'Software Name' ), 'readonly' => true, 'value' => 'WordPress', ), 'software_version' => array( 'desc' => __( 'Software Version' ), 'readonly' => true, 'value' => get_bloginfo( 'version' ), ), 'blog_url' => array( 'desc' => __( 'WordPress Address (URL)' ), 'readonly' => true, 'option' => 'siteurl', ), 'home_url' => array( 'desc' => __( 'Site Address (URL)' ), 'readonly' => true, 'option' => 'home', ), 'login_url' => array( 'desc' => __( 'Login Address (URL)' ), 'readonly' => true, 'value' => wp_login_url(), ), 'admin_url' => array( 'desc' => __( 'The URL to the admin area' ), 'readonly' => true, 'value' => get_admin_url(), ), 'image_default_link_type' => array( 'desc' => __( 'Image default link type' ), 'readonly' => true, 'option' => 'image_default_link_type', ), 'image_default_size' => array( 'desc' => __( 'Image default size' ), 'readonly' => true, 'option' => 'image_default_size', ), 'image_default_align' => array( 'desc' => __( 'Image default align' ), 'readonly' => true, 'option' => 'image_default_align', ), 'template' => array( 'desc' => __( 'Template' ), 'readonly' => true, 'option' => 'template', ), 'stylesheet' => array( 'desc' => __( 'Stylesheet' ), 'readonly' => true, 'option' => 'stylesheet', ), 'post_thumbnail' => array( 'desc' => __( 'Post Thumbnail' ), 'readonly' => true, 'value' => current_theme_supports( 'post-thumbnails' ), ), // Updatable options. 'time_zone' => array( 'desc' => __( 'Time Zone' ), 'readonly' => false, 'option' => 'gmt_offset', ), 'blog_title' => array( 'desc' => __( 'Site Title' ), 'readonly' => false, 'option' => 'blogname', ), 'blog_tagline' => array( 'desc' => __( 'Site Tagline' ), 'readonly' => false, 'option' => 'blogdescription', ), 'date_format' => array( 'desc' => __( 'Date Format' ), 'readonly' => false, 'option' => 'date_format', ), 'time_format' => array( 'desc' => __( 'Time Format' ), 'readonly' => false, 'option' => 'time_format', ), 'users_can_register' => array( 'desc' => __( 'Allow new users to sign up' ), 'readonly' => false, 'option' => 'users_can_register', ), 'thumbnail_size_w' => array( 'desc' => __( 'Thumbnail Width' ), 'readonly' => false, 'option' => 'thumbnail_size_w', ), 'thumbnail_size_h' => array( 'desc' => __( 'Thumbnail Height' ), 'readonly' => false, 'option' => 'thumbnail_size_h', ), 'thumbnail_crop' => array( 'desc' => __( 'Crop thumbnail to exact dimensions' ), 'readonly' => false, 'option' => 'thumbnail_crop', ), 'medium_size_w' => array( 'desc' => __( 'Medium size image width' ), 'readonly' => false, 'option' => 'medium_size_w', ), 'medium_size_h' => array( 'desc' => __( 'Medium size image height' ), 'readonly' => false, 'option' => 'medium_size_h', ), 'medium_large_size_w' => array( 'desc' => __( 'Medium-Large size image width' ), 'readonly' => false, 'option' => 'medium_large_size_w', ), 'medium_large_size_h' => array( 'desc' => __( 'Medium-Large size image height' ), 'readonly' => false, 'option' => 'medium_large_size_h', ), 'large_size_w' => array( 'desc' => __( 'Large size image width' ), 'readonly' => false, 'option' => 'large_size_w', ), 'large_size_h' => array( 'desc' => __( 'Large size image height' ), 'readonly' => false, 'option' => 'large_size_h', ), 'default_comment_status' => array( 'desc' => __( 'Allow people to submit comments on new posts.' ), 'readonly' => false, 'option' => 'default_comment_status', ), 'default_ping_status' => array( 'desc' => __( 'Allow link notifications from other blogs (pingbacks and trackbacks) on new posts.' ), 'readonly' => false, 'option' => 'default_ping_status', ), ); /** * Filters the XML-RPC blog options property. * * @since 2.6.0 * * @param array $blog_options An array of XML-RPC blog options. */ $this->blog_options = apply_filters( 'xmlrpc_blog_options', $this->blog_options ); } /** * Retrieves the blogs of the user. * * @since 2.6.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type string $0 Username. * @type string $1 Password. * } * @return array|IXR_Error Array contains: * - 'isAdmin' * - 'isPrimary' - whether the blog is the user's primary blog * - 'url' * - 'blogid' * - 'blogName' * - 'xmlrpc' - url of xmlrpc endpoint */ public function wp_getUsersBlogs( $args ) { if ( ! $this->minimum_args( $args, 2 ) ) { return $this->error; } // If this isn't on WPMU then just use blogger_getUsersBlogs(). if ( ! is_multisite() ) { array_unshift( $args, 1 ); return $this->blogger_getUsersBlogs( $args ); } $this->escape( $args ); $username = $args[0]; $password = $args[1]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** * Fires after the XML-RPC user has been authenticated but before the rest of * the method logic begins. * * All built-in XML-RPC methods use the action xmlrpc_call, with a parameter * equal to the method's name, e.g., wp.getUsersBlogs, wp.newPost, etc. * * @since 2.5.0 * @since 5.7.0 Added the `$args` and `$server` parameters. * * @param string $name The method name. * @param array|string $args The escaped arguments passed to the method. * @param wp_xmlrpc_server $server The XML-RPC server instance. */ do_action( 'xmlrpc_call', 'wp.getUsersBlogs', $args, $this ); $blogs = (array) get_blogs_of_user( $user->ID ); $struct = array(); $primary_blog_id = 0; $active_blog = get_active_blog_for_user( $user->ID ); if ( $active_blog ) { $primary_blog_id = (int) $active_blog->blog_id; } foreach ( $blogs as $blog ) { // Don't include blogs that aren't hosted at this site. if ( get_current_network_id() != $blog->site_id ) { continue; } $blog_id = $blog->userblog_id; switch_to_blog( $blog_id ); $is_admin = current_user_can( 'manage_options' ); $is_primary = ( (int) $blog_id === $primary_blog_id ); $struct[] = array( 'isAdmin' => $is_admin, 'isPrimary' => $is_primary, 'url' => home_url( '/' ), 'blogid' => (string) $blog_id, 'blogName' => get_option( 'blogname' ), 'xmlrpc' => site_url( 'xmlrpc.php', 'rpc' ), ); restore_current_blog(); } return $struct; } /** * Checks if the method received at least the minimum number of arguments. * * @since 3.4.0 * * @param array $args An array of arguments to check. * @param int $count Minimum number of arguments. * @return bool True if `$args` contains at least `$count` arguments, false otherwise. */ protected function minimum_args( $args, $count ) { if ( ! is_array( $args ) || count( $args ) < $count ) { $this->error = new IXR_Error( 400, __( 'Insufficient arguments passed to this XML-RPC method.' ) ); return false; } return true; } /** * Prepares taxonomy data for return in an XML-RPC object. * * @param WP_Taxonomy $taxonomy The unprepared taxonomy data. * @param array $fields The subset of taxonomy fields to return. * @return array The prepared taxonomy data. */ protected function _prepare_taxonomy( $taxonomy, $fields ) { $_taxonomy = array( 'name' => $taxonomy->name, 'label' => $taxonomy->label, 'hierarchical' => (bool) $taxonomy->hierarchical, 'public' => (bool) $taxonomy->public, 'show_ui' => (bool) $taxonomy->show_ui, '_builtin' => (bool) $taxonomy->_builtin, ); if ( in_array( 'labels', $fields, true ) ) { $_taxonomy['labels'] = (array) $taxonomy->labels; } if ( in_array( 'cap', $fields, true ) ) { $_taxonomy['cap'] = (array) $taxonomy->cap; } if ( in_array( 'menu', $fields, true ) ) { $_taxonomy['show_in_menu'] = (bool) $taxonomy->show_in_menu; } if ( in_array( 'object_type', $fields, true ) ) { $_taxonomy['object_type'] = array_unique( (array) $taxonomy->object_type ); } /** * Filters XML-RPC-prepared data for the given taxonomy. * * @since 3.4.0 * * @param array $_taxonomy An array of taxonomy data. * @param WP_Taxonomy $taxonomy Taxonomy object. * @param array $fields The subset of taxonomy fields to return. */ return apply_filters( 'xmlrpc_prepare_taxonomy', $_taxonomy, $taxonomy, $fields ); } /** * Prepares term data for return in an XML-RPC object. * * @param array|object $term The unprepared term data. * @return array The prepared term data. */ protected function _prepare_term( $term ) { $_term = $term; if ( ! is_array( $_term ) ) { $_term = get_object_vars( $_term ); } // For integers which may be larger than XML-RPC supports ensure we return strings. $_term['term_id'] = (string) $_term['term_id']; $_term['term_group'] = (string) $_term['term_group']; $_term['term_taxonomy_id'] = (string) $_term['term_taxonomy_id']; $_term['parent'] = (string) $_term['parent']; // Count we are happy to return as an integer because people really shouldn't use terms that much. $_term['count'] = (int) $_term['count']; // Get term meta. $_term['custom_fields'] = $this->get_term_custom_fields( $_term['term_id'] ); /** * Filters XML-RPC-prepared data for the given term. * * @since 3.4.0 * * @param array $_term An array of term data. * @param array|object $term Term object or array. */ return apply_filters( 'xmlrpc_prepare_term', $_term, $term ); } /** * Converts a WordPress date string to an IXR_Date object. * * @param string $date Date string to convert. * @return IXR_Date IXR_Date object. */ protected function _convert_date( $date ) { if ( '0000-00-00 00:00:00' === $date ) { return new IXR_Date( '00000000T00:00:00Z' ); } return new IXR_Date( mysql2date( 'Ymd\TH:i:s', $date, false ) ); } /** * Converts a WordPress GMT date string to an IXR_Date object. * * @param string $date_gmt WordPress GMT date string. * @param string $date Date string. * @return IXR_Date IXR_Date object. */ protected function _convert_date_gmt( $date_gmt, $date ) { if ( '0000-00-00 00:00:00' !== $date && '0000-00-00 00:00:00' === $date_gmt ) { return new IXR_Date( get_gmt_from_date( mysql2date( 'Y-m-d H:i:s', $date, false ), 'Ymd\TH:i:s' ) ); } return $this->_convert_date( $date_gmt ); } /** * Prepares post data for return in an XML-RPC object. * * @param array $post The unprepared post data. * @param array $fields The subset of post type fields to return. * @return array The prepared post data. */ protected function _prepare_post( $post, $fields ) { // Holds the data for this post. built up based on $fields. $_post = array( 'post_id' => (string) $post['ID'] ); // Prepare common post fields. $post_fields = array( 'post_title' => $post['post_title'], 'post_date' => $this->_convert_date( $post['post_date'] ), 'post_date_gmt' => $this->_convert_date_gmt( $post['post_date_gmt'], $post['post_date'] ), 'post_modified' => $this->_convert_date( $post['post_modified'] ), 'post_modified_gmt' => $this->_convert_date_gmt( $post['post_modified_gmt'], $post['post_modified'] ), 'post_status' => $post['post_status'], 'post_type' => $post['post_type'], 'post_name' => $post['post_name'], 'post_author' => $post['post_author'], 'post_password' => $post['post_password'], 'post_excerpt' => $post['post_excerpt'], 'post_content' => $post['post_content'], 'post_parent' => (string) $post['post_parent'], 'post_mime_type' => $post['post_mime_type'], 'link' => get_permalink( $post['ID'] ), 'guid' => $post['guid'], 'menu_order' => (int) $post['menu_order'], 'comment_status' => $post['comment_status'], 'ping_status' => $post['ping_status'], 'sticky' => ( 'post' === $post['post_type'] && is_sticky( $post['ID'] ) ), ); // Thumbnail. $post_fields['post_thumbnail'] = array(); $thumbnail_id = get_post_thumbnail_id( $post['ID'] ); if ( $thumbnail_id ) { $thumbnail_size = current_theme_supports( 'post-thumbnail' ) ? 'post-thumbnail' : 'thumbnail'; $post_fields['post_thumbnail'] = $this->_prepare_media_item( get_post( $thumbnail_id ), $thumbnail_size ); } // Consider future posts as published. if ( 'future' === $post_fields['post_status'] ) { $post_fields['post_status'] = 'publish'; } // Fill in blank post format. $post_fields['post_format'] = get_post_format( $post['ID'] ); if ( empty( $post_fields['post_format'] ) ) { $post_fields['post_format'] = 'standard'; } // Merge requested $post_fields fields into $_post. if ( in_array( 'post', $fields, true ) ) { $_post = array_merge( $_post, $post_fields ); } else { $requested_fields = array_intersect_key( $post_fields, array_flip( $fields ) ); $_post = array_merge( $_post, $requested_fields ); } $all_taxonomy_fields = in_array( 'taxonomies', $fields, true ); if ( $all_taxonomy_fields || in_array( 'terms', $fields, true ) ) { $post_type_taxonomies = get_object_taxonomies( $post['post_type'], 'names' ); $terms = wp_get_object_terms( $post['ID'], $post_type_taxonomies ); $_post['terms'] = array(); foreach ( $terms as $term ) { $_post['terms'][] = $this->_prepare_term( $term ); } } if ( in_array( 'custom_fields', $fields, true ) ) { $_post['custom_fields'] = $this->get_custom_fields( $post['ID'] ); } if ( in_array( 'enclosure', $fields, true ) ) { $_post['enclosure'] = array(); $enclosures = (array) get_post_meta( $post['ID'], 'enclosure' ); if ( ! empty( $enclosures ) ) { $encdata = explode( "\n", $enclosures[0] ); $_post['enclosure']['url'] = trim( htmlspecialchars( $encdata[0] ) ); $_post['enclosure']['length'] = (int) trim( $encdata[1] ); $_post['enclosure']['type'] = trim( $encdata[2] ); } } /** * Filters XML-RPC-prepared date for the given post. * * @since 3.4.0 * * @param array $_post An array of modified post data. * @param array $post An array of post data. * @param array $fields An array of post fields. */ return apply_filters( 'xmlrpc_prepare_post', $_post, $post, $fields ); } /** * Prepares post data for return in an XML-RPC object. * * @since 3.4.0 * @since 4.6.0 Converted the `$post_type` parameter to accept a WP_Post_Type object. * * @param WP_Post_Type $post_type Post type object. * @param array $fields The subset of post fields to return. * @return array The prepared post type data. */ protected function _prepare_post_type( $post_type, $fields ) { $_post_type = array( 'name' => $post_type->name, 'label' => $post_type->label, 'hierarchical' => (bool) $post_type->hierarchical, 'public' => (bool) $post_type->public, 'show_ui' => (bool) $post_type->show_ui, '_builtin' => (bool) $post_type->_builtin, 'has_archive' => (bool) $post_type->has_archive, 'supports' => get_all_post_type_supports( $post_type->name ), ); if ( in_array( 'labels', $fields, true ) ) { $_post_type['labels'] = (array) $post_type->labels; } if ( in_array( 'cap', $fields, true ) ) { $_post_type['cap'] = (array) $post_type->cap; $_post_type['map_meta_cap'] = (bool) $post_type->map_meta_cap; } if ( in_array( 'menu', $fields, true ) ) { $_post_type['menu_position'] = (int) $post_type->menu_position; $_post_type['menu_icon'] = $post_type->menu_icon; $_post_type['show_in_menu'] = (bool) $post_type->show_in_menu; } if ( in_array( 'taxonomies', $fields, true ) ) { $_post_type['taxonomies'] = get_object_taxonomies( $post_type->name, 'names' ); } /** * Filters XML-RPC-prepared date for the given post type. * * @since 3.4.0 * @since 4.6.0 Converted the `$post_type` parameter to accept a WP_Post_Type object. * * @param array $_post_type An array of post type data. * @param WP_Post_Type $post_type Post type object. */ return apply_filters( 'xmlrpc_prepare_post_type', $_post_type, $post_type ); } /** * Prepares media item data for return in an XML-RPC object. * * @param WP_Post $media_item The unprepared media item data. * @param string $thumbnail_size The image size to use for the thumbnail URL. * @return array The prepared media item data. */ protected function _prepare_media_item( $media_item, $thumbnail_size = 'thumbnail' ) { $_media_item = array( 'attachment_id' => (string) $media_item->ID, 'date_created_gmt' => $this->_convert_date_gmt( $media_item->post_date_gmt, $media_item->post_date ), 'parent' => $media_item->post_parent, 'link' => wp_get_attachment_url( $media_item->ID ), 'title' => $media_item->post_title, 'caption' => $media_item->post_excerpt, 'description' => $media_item->post_content, 'metadata' => wp_get_attachment_metadata( $media_item->ID ), 'type' => $media_item->post_mime_type, 'alt' => get_post_meta( $media_item->ID, '_wp_attachment_image_alt', true ), ); $thumbnail_src = image_downsize( $media_item->ID, $thumbnail_size ); if ( $thumbnail_src ) { $_media_item['thumbnail'] = $thumbnail_src[0]; } else { $_media_item['thumbnail'] = $_media_item['link']; } /** * Filters XML-RPC-prepared data for the given media item. * * @since 3.4.0 * * @param array $_media_item An array of media item data. * @param WP_Post $media_item Media item object. * @param string $thumbnail_size Image size. */ return apply_filters( 'xmlrpc_prepare_media_item', $_media_item, $media_item, $thumbnail_size ); } /** * Prepares page data for return in an XML-RPC object. * * @param WP_Post $page The unprepared page data. * @return array The prepared page data. */ protected function _prepare_page( $page ) { // Get all of the page content and link. $full_page = get_extended( $page->post_content ); $link = get_permalink( $page->ID ); // Get info the page parent if there is one. $parent_title = ''; if ( ! empty( $page->post_parent ) ) { $parent = get_post( $page->post_parent ); $parent_title = $parent->post_title; } // Determine comment and ping settings. $allow_comments = comments_open( $page->ID ) ? 1 : 0; $allow_pings = pings_open( $page->ID ) ? 1 : 0; // Format page date. $page_date = $this->_convert_date( $page->post_date ); $page_date_gmt = $this->_convert_date_gmt( $page->post_date_gmt, $page->post_date ); // Pull the categories info together. $categories = array(); if ( is_object_in_taxonomy( 'page', 'category' ) ) { foreach ( wp_get_post_categories( $page->ID ) as $cat_id ) { $categories[] = get_cat_name( $cat_id ); } } // Get the author info. $author = get_userdata( $page->post_author ); $page_template = get_page_template_slug( $page->ID ); if ( empty( $page_template ) ) { $page_template = 'default'; } $_page = array( 'dateCreated' => $page_date, 'userid' => $page->post_author, 'page_id' => $page->ID, 'page_status' => $page->post_status, 'description' => $full_page['main'], 'title' => $page->post_title, 'link' => $link, 'permaLink' => $link, 'categories' => $categories, 'excerpt' => $page->post_excerpt, 'text_more' => $full_page['extended'], 'mt_allow_comments' => $allow_comments, 'mt_allow_pings' => $allow_pings, 'wp_slug' => $page->post_name, 'wp_password' => $page->post_password, 'wp_author' => $author->display_name, 'wp_page_parent_id' => $page->post_parent, 'wp_page_parent_title' => $parent_title, 'wp_page_order' => $page->menu_order, 'wp_author_id' => (string) $author->ID, 'wp_author_display_name' => $author->display_name, 'date_created_gmt' => $page_date_gmt, 'custom_fields' => $this->get_custom_fields( $page->ID ), 'wp_page_template' => $page_template, ); /** * Filters XML-RPC-prepared data for the given page. * * @since 3.4.0 * * @param array $_page An array of page data. * @param WP_Post $page Page object. */ return apply_filters( 'xmlrpc_prepare_page', $_page, $page ); } /** * Prepares comment data for return in an XML-RPC object. * * @param WP_Comment $comment The unprepared comment data. * @return array The prepared comment data. */ protected function _prepare_comment( $comment ) { // Format page date. $comment_date_gmt = $this->_convert_date_gmt( $comment->comment_date_gmt, $comment->comment_date ); if ( '0' == $comment->comment_approved ) { $comment_status = 'hold'; } elseif ( 'spam' === $comment->comment_approved ) { $comment_status = 'spam'; } elseif ( '1' == $comment->comment_approved ) { $comment_status = 'approve'; } else { $comment_status = $comment->comment_approved; } $_comment = array( 'date_created_gmt' => $comment_date_gmt, 'user_id' => $comment->user_id, 'comment_id' => $comment->comment_ID, 'parent' => $comment->comment_parent, 'status' => $comment_status, 'content' => $comment->comment_content, 'link' => get_comment_link( $comment ), 'post_id' => $comment->comment_post_ID, 'post_title' => get_the_title( $comment->comment_post_ID ), 'author' => $comment->comment_author, 'author_url' => $comment->comment_author_url, 'author_email' => $comment->comment_author_email, 'author_ip' => $comment->comment_author_IP, 'type' => $comment->comment_type, ); /** * Filters XML-RPC-prepared data for the given comment. * * @since 3.4.0 * * @param array $_comment An array of prepared comment data. * @param WP_Comment $comment Comment object. */ return apply_filters( 'xmlrpc_prepare_comment', $_comment, $comment ); } /** * Prepares user data for return in an XML-RPC object. * * @param WP_User $user The unprepared user object. * @param array $fields The subset of user fields to return. * @return array The prepared user data. */ protected function _prepare_user( $user, $fields ) { $_user = array( 'user_id' => (string) $user->ID ); $user_fields = array( 'username' => $user->user_login, 'first_name' => $user->user_firstname, 'last_name' => $user->user_lastname, 'registered' => $this->_convert_date( $user->user_registered ), 'bio' => $user->user_description, 'email' => $user->user_email, 'nickname' => $user->nickname, 'nicename' => $user->user_nicename, 'url' => $user->user_url, 'display_name' => $user->display_name, 'roles' => $user->roles, ); if ( in_array( 'all', $fields, true ) ) { $_user = array_merge( $_user, $user_fields ); } else { if ( in_array( 'basic', $fields, true ) ) { $basic_fields = array( 'username', 'email', 'registered', 'display_name', 'nicename' ); $fields = array_merge( $fields, $basic_fields ); } $requested_fields = array_intersect_key( $user_fields, array_flip( $fields ) ); $_user = array_merge( $_user, $requested_fields ); } /** * Filters XML-RPC-prepared data for the given user. * * @since 3.5.0 * * @param array $_user An array of user data. * @param WP_User $user User object. * @param array $fields An array of user fields. */ return apply_filters( 'xmlrpc_prepare_user', $_user, $user, $fields ); } /** * Creates a new post for any registered post type. * * @since 3.4.0 * * @link https://en.wikipedia.org/wiki/RSS_enclosure for information on RSS enclosures. * * @param array $args { * Method arguments. Note: top-level arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * @type array $3 { * Content struct for adding a new post. See wp_insert_post() for information on * additional post fields * * @type string $post_type Post type. Default 'post'. * @type string $post_status Post status. Default 'draft' * @type string $post_title Post title. * @type int $post_author Post author ID. * @type string $post_excerpt Post excerpt. * @type string $post_content Post content. * @type string $post_date_gmt Post date in GMT. * @type string $post_date Post date. * @type string $post_password Post password (20-character limit). * @type string $comment_status Post comment enabled status. Accepts 'open' or 'closed'. * @type string $ping_status Post ping status. Accepts 'open' or 'closed'. * @type bool $sticky Whether the post should be sticky. Automatically false if * `$post_status` is 'private'. * @type int $post_thumbnail ID of an image to use as the post thumbnail/featured image. * @type array $custom_fields Array of meta key/value pairs to add to the post. * @type array $terms Associative array with taxonomy names as keys and arrays * of term IDs as values. * @type array $terms_names Associative array with taxonomy names as keys and arrays * of term names as values. * @type array $enclosure { * Array of feed enclosure data to add to post meta. * * @type string $url URL for the feed enclosure. * @type int $length Size in bytes of the enclosure. * @type string $type Mime-type for the enclosure. * } * } * } * @return int|IXR_Error Post ID on success, IXR_Error instance otherwise. */ public function wp_newPost( $args ) { if ( ! $this->minimum_args( $args, 4 ) ) { return $this->error; } $this->escape( $args ); $username = $args[1]; $password = $args[2]; $content_struct = $args[3]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } // Convert the date field back to IXR form. if ( isset( $content_struct['post_date'] ) && ! ( $content_struct['post_date'] instanceof IXR_Date ) ) { $content_struct['post_date'] = $this->_convert_date( $content_struct['post_date'] ); } /* * Ignore the existing GMT date if it is empty or a non-GMT date was supplied in $content_struct, * since _insert_post() will ignore the non-GMT date if the GMT date is set. */ if ( isset( $content_struct['post_date_gmt'] ) && ! ( $content_struct['post_date_gmt'] instanceof IXR_Date ) ) { if ( '0000-00-00 00:00:00' === $content_struct['post_date_gmt'] || isset( $content_struct['post_date'] ) ) { unset( $content_struct['post_date_gmt'] ); } else { $content_struct['post_date_gmt'] = $this->_convert_date( $content_struct['post_date_gmt'] ); } } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.newPost', $args, $this ); unset( $content_struct['ID'] ); return $this->_insert_post( $user, $content_struct ); } /** * Helper method for filtering out elements from an array. * * @since 3.4.0 * * @param int $count Number to compare to one. * @return bool True if the number is greater than one, false otherwise. */ private function _is_greater_than_one( $count ) { return $count > 1; } /** * Encapsulates the logic for sticking a post and determining if * the user has permission to do so. * * @since 4.3.0 * * @param array $post_data * @param bool $update * @return void|IXR_Error */ private function _toggle_sticky( $post_data, $update = false ) { $post_type = get_post_type_object( $post_data['post_type'] ); // Private and password-protected posts cannot be stickied. if ( 'private' === $post_data['post_status'] || ! empty( $post_data['post_password'] ) ) { // Error if the client tried to stick the post, otherwise, silently unstick. if ( ! empty( $post_data['sticky'] ) ) { return new IXR_Error( 401, __( 'Sorry, you cannot stick a private post.' ) ); } if ( $update ) { unstick_post( $post_data['ID'] ); } } elseif ( isset( $post_data['sticky'] ) ) { if ( ! current_user_can( $post_type->cap->edit_others_posts ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to make posts sticky.' ) ); } $sticky = wp_validate_boolean( $post_data['sticky'] ); if ( $sticky ) { stick_post( $post_data['ID'] ); } else { unstick_post( $post_data['ID'] ); } } } /** * Helper method for wp_newPost() and wp_editPost(), containing shared logic. * * @since 3.4.0 * * @see wp_insert_post() * * @param WP_User $user The post author if post_author isn't set in $content_struct. * @param array|IXR_Error $content_struct Post data to insert. * @return IXR_Error|string */ protected function _insert_post( $user, $content_struct ) { $defaults = array( 'post_status' => 'draft', 'post_type' => 'post', 'post_author' => 0, 'post_password' => '', 'post_excerpt' => '', 'post_content' => '', 'post_title' => '', 'post_date' => '', 'post_date_gmt' => '', 'post_format' => null, 'post_name' => null, 'post_thumbnail' => null, 'post_parent' => 0, 'ping_status' => '', 'comment_status' => '', 'custom_fields' => null, 'terms_names' => null, 'terms' => null, 'sticky' => null, 'enclosure' => null, 'ID' => null, ); $post_data = wp_parse_args( array_intersect_key( $content_struct, $defaults ), $defaults ); $post_type = get_post_type_object( $post_data['post_type'] ); if ( ! $post_type ) { return new IXR_Error( 403, __( 'Invalid post type.' ) ); } $update = ! empty( $post_data['ID'] ); if ( $update ) { if ( ! get_post( $post_data['ID'] ) ) { return new IXR_Error( 401, __( 'Invalid post ID.' ) ); } if ( ! current_user_can( 'edit_post', $post_data['ID'] ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) ); } if ( get_post_type( $post_data['ID'] ) !== $post_data['post_type'] ) { return new IXR_Error( 401, __( 'The post type may not be changed.' ) ); } } else { if ( ! current_user_can( $post_type->cap->create_posts ) || ! current_user_can( $post_type->cap->edit_posts ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to post on this site.' ) ); } } switch ( $post_data['post_status'] ) { case 'draft': case 'pending': break; case 'private': if ( ! current_user_can( $post_type->cap->publish_posts ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to create private posts in this post type.' ) ); } break; case 'publish': case 'future': if ( ! current_user_can( $post_type->cap->publish_posts ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to publish posts in this post type.' ) ); } break; default: if ( ! get_post_status_object( $post_data['post_status'] ) ) { $post_data['post_status'] = 'draft'; } break; } if ( ! empty( $post_data['post_password'] ) && ! current_user_can( $post_type->cap->publish_posts ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to create password protected posts in this post type.' ) ); } $post_data['post_author'] = absint( $post_data['post_author'] ); if ( ! empty( $post_data['post_author'] ) && $post_data['post_author'] != $user->ID ) { if ( ! current_user_can( $post_type->cap->edit_others_posts ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to create posts as this user.' ) ); } $author = get_userdata( $post_data['post_author'] ); if ( ! $author ) { return new IXR_Error( 404, __( 'Invalid author ID.' ) ); } } else { $post_data['post_author'] = $user->ID; } if ( 'open' !== $post_data['comment_status'] && 'closed' !== $post_data['comment_status'] ) { unset( $post_data['comment_status'] ); } if ( 'open' !== $post_data['ping_status'] && 'closed' !== $post_data['ping_status'] ) { unset( $post_data['ping_status'] ); } // Do some timestamp voodoo. if ( ! empty( $post_data['post_date_gmt'] ) ) { // We know this is supposed to be GMT, so we're going to slap that Z on there by force. $dateCreated = rtrim( $post_data['post_date_gmt']->getIso(), 'Z' ) . 'Z'; } elseif ( ! empty( $post_data['post_date'] ) ) { $dateCreated = $post_data['post_date']->getIso(); } // Default to not flagging the post date to be edited unless it's intentional. $post_data['edit_date'] = false; if ( ! empty( $dateCreated ) ) { $post_data['post_date'] = iso8601_to_datetime( $dateCreated ); $post_data['post_date_gmt'] = iso8601_to_datetime( $dateCreated, 'gmt' ); // Flag the post date to be edited. $post_data['edit_date'] = true; } if ( ! isset( $post_data['ID'] ) ) { $post_data['ID'] = get_default_post_to_edit( $post_data['post_type'], true )->ID; } $post_id = $post_data['ID']; if ( 'post' === $post_data['post_type'] ) { $error = $this->_toggle_sticky( $post_data, $update ); if ( $error ) { return $error; } } if ( isset( $post_data['post_thumbnail'] ) ) { // Empty value deletes, non-empty value adds/updates. if ( ! $post_data['post_thumbnail'] ) { delete_post_thumbnail( $post_id ); } elseif ( ! get_post( absint( $post_data['post_thumbnail'] ) ) ) { return new IXR_Error( 404, __( 'Invalid attachment ID.' ) ); } set_post_thumbnail( $post_id, $post_data['post_thumbnail'] ); unset( $content_struct['post_thumbnail'] ); } if ( isset( $post_data['custom_fields'] ) ) { $this->set_custom_fields( $post_id, $post_data['custom_fields'] ); } if ( isset( $post_data['terms'] ) || isset( $post_data['terms_names'] ) ) { $post_type_taxonomies = get_object_taxonomies( $post_data['post_type'], 'objects' ); // Accumulate term IDs from terms and terms_names. $terms = array(); // First validate the terms specified by ID. if ( isset( $post_data['terms'] ) && is_array( $post_data['terms'] ) ) { $taxonomies = array_keys( $post_data['terms'] ); // Validating term IDs. foreach ( $taxonomies as $taxonomy ) { if ( ! array_key_exists( $taxonomy, $post_type_taxonomies ) ) { return new IXR_Error( 401, __( 'Sorry, one of the given taxonomies is not supported by the post type.' ) ); } if ( ! current_user_can( $post_type_taxonomies[ $taxonomy ]->cap->assign_terms ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to assign a term to one of the given taxonomies.' ) ); } $term_ids = $post_data['terms'][ $taxonomy ]; $terms[ $taxonomy ] = array(); foreach ( $term_ids as $term_id ) { $term = get_term_by( 'id', $term_id, $taxonomy ); if ( ! $term ) { return new IXR_Error( 403, __( 'Invalid term ID.' ) ); } $terms[ $taxonomy ][] = (int) $term_id; } } } // Now validate terms specified by name. if ( isset( $post_data['terms_names'] ) && is_array( $post_data['terms_names'] ) ) { $taxonomies = array_keys( $post_data['terms_names'] ); foreach ( $taxonomies as $taxonomy ) { if ( ! array_key_exists( $taxonomy, $post_type_taxonomies ) ) { return new IXR_Error( 401, __( 'Sorry, one of the given taxonomies is not supported by the post type.' ) ); } if ( ! current_user_can( $post_type_taxonomies[ $taxonomy ]->cap->assign_terms ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to assign a term to one of the given taxonomies.' ) ); } /* * For hierarchical taxonomies, we can't assign a term when multiple terms * in the hierarchy share the same name. */ $ambiguous_terms = array(); if ( is_taxonomy_hierarchical( $taxonomy ) ) { $tax_term_names = get_terms( array( 'taxonomy' => $taxonomy, 'fields' => 'names', 'hide_empty' => false, ) ); // Count the number of terms with the same name. $tax_term_names_count = array_count_values( $tax_term_names ); // Filter out non-ambiguous term names. $ambiguous_tax_term_counts = array_filter( $tax_term_names_count, array( $this, '_is_greater_than_one' ) ); $ambiguous_terms = array_keys( $ambiguous_tax_term_counts ); } $term_names = $post_data['terms_names'][ $taxonomy ]; foreach ( $term_names as $term_name ) { if ( in_array( $term_name, $ambiguous_terms, true ) ) { return new IXR_Error( 401, __( 'Ambiguous term name used in a hierarchical taxonomy. Please use term ID instead.' ) ); } $term = get_term_by( 'name', $term_name, $taxonomy ); if ( ! $term ) { // Term doesn't exist, so check that the user is allowed to create new terms. if ( ! current_user_can( $post_type_taxonomies[ $taxonomy ]->cap->edit_terms ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to add a term to one of the given taxonomies.' ) ); } // Create the new term. $term_info = wp_insert_term( $term_name, $taxonomy ); if ( is_wp_error( $term_info ) ) { return new IXR_Error( 500, $term_info->get_error_message() ); } $terms[ $taxonomy ][] = (int) $term_info['term_id']; } else { $terms[ $taxonomy ][] = (int) $term->term_id; } } } } $post_data['tax_input'] = $terms; unset( $post_data['terms'], $post_data['terms_names'] ); } if ( isset( $post_data['post_format'] ) ) { $format = set_post_format( $post_id, $post_data['post_format'] ); if ( is_wp_error( $format ) ) { return new IXR_Error( 500, $format->get_error_message() ); } unset( $post_data['post_format'] ); } // Handle enclosures. $enclosure = isset( $post_data['enclosure'] ) ? $post_data['enclosure'] : null; $this->add_enclosure_if_new( $post_id, $enclosure ); $this->attach_uploads( $post_id, $post_data['post_content'] ); /** * Filters post data array to be inserted via XML-RPC. * * @since 3.4.0 * * @param array $post_data Parsed array of post data. * @param array $content_struct Post data array. */ $post_data = apply_filters( 'xmlrpc_wp_insert_post_data', $post_data, $content_struct ); // Remove all null values to allow for using the insert/update post default values for those keys instead. $post_data = array_filter( $post_data, static function ( $value ) { return null !== $value; } ); $post_id = $update ? wp_update_post( $post_data, true ) : wp_insert_post( $post_data, true ); if ( is_wp_error( $post_id ) ) { return new IXR_Error( 500, $post_id->get_error_message() ); } if ( ! $post_id ) { if ( $update ) { return new IXR_Error( 401, __( 'Sorry, the post could not be updated.' ) ); } else { return new IXR_Error( 401, __( 'Sorry, the post could not be created.' ) ); } } return (string) $post_id; } /** * Edits a post for any registered post type. * * The $content_struct parameter only needs to contain fields that * should be changed. All other fields will retain their existing values. * * @since 3.4.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * @type int $3 Post ID. * @type array $4 Extra content arguments. * } * @return true|IXR_Error True on success, IXR_Error on failure. */ public function wp_editPost( $args ) { if ( ! $this->minimum_args( $args, 5 ) ) { return $this->error; } $this->escape( $args ); $username = $args[1]; $password = $args[2]; $post_id = (int) $args[3]; $content_struct = $args[4]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.editPost', $args, $this ); $post = get_post( $post_id, ARRAY_A ); if ( empty( $post['ID'] ) ) { return new IXR_Error( 404, __( 'Invalid post ID.' ) ); } if ( isset( $content_struct['if_not_modified_since'] ) ) { // If the post has been modified since the date provided, return an error. if ( mysql2date( 'U', $post['post_modified_gmt'] ) > $content_struct['if_not_modified_since']->getTimestamp() ) { return new IXR_Error( 409, __( 'There is a revision of this post that is more recent.' ) ); } } // Convert the date field back to IXR form. $post['post_date'] = $this->_convert_date( $post['post_date'] ); /* * Ignore the existing GMT date if it is empty or a non-GMT date was supplied in $content_struct, * since _insert_post() will ignore the non-GMT date if the GMT date is set. */ if ( '0000-00-00 00:00:00' === $post['post_date_gmt'] || isset( $content_struct['post_date'] ) ) { unset( $post['post_date_gmt'] ); } else { $post['post_date_gmt'] = $this->_convert_date( $post['post_date_gmt'] ); } /* * If the API client did not provide 'post_date', then we must not perpetuate the value that * was stored in the database, or it will appear to be an intentional edit. Conveying it here * as if it was coming from the API client will cause an otherwise zeroed out 'post_date_gmt' * to get set with the value that was originally stored in the database when the draft was created. */ if ( ! isset( $content_struct['post_date'] ) ) { unset( $post['post_date'] ); } $this->escape( $post ); $merged_content_struct = array_merge( $post, $content_struct ); $retval = $this->_insert_post( $user, $merged_content_struct ); if ( $retval instanceof IXR_Error ) { return $retval; } return true; } /** * Deletes a post for any registered post type. * * @since 3.4.0 * * @see wp_delete_post() * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * @type int $3 Post ID. * } * @return true|IXR_Error True on success, IXR_Error instance on failure. */ public function wp_deletePost( $args ) { if ( ! $this->minimum_args( $args, 4 ) ) { return $this->error; } $this->escape( $args ); $username = $args[1]; $password = $args[2]; $post_id = (int) $args[3]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.deletePost', $args, $this ); $post = get_post( $post_id, ARRAY_A ); if ( empty( $post['ID'] ) ) { return new IXR_Error( 404, __( 'Invalid post ID.' ) ); } if ( ! current_user_can( 'delete_post', $post_id ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to delete this post.' ) ); } $result = wp_delete_post( $post_id ); if ( ! $result ) { return new IXR_Error( 500, __( 'Sorry, the post could not be deleted.' ) ); } return true; } /** * Retrieves a post. * * @since 3.4.0 * * The optional $fields parameter specifies what fields will be included * in the response array. This should be a list of field names. 'post_id' will * always be included in the response regardless of the value of $fields. * * Instead of, or in addition to, individual field names, conceptual group * names can be used to specify multiple fields. The available conceptual * groups are 'post' (all basic fields), 'taxonomies', 'custom_fields', * and 'enclosure'. * * @see get_post() * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * @type int $3 Post ID. * @type array $4 Optional. The subset of post type fields to return. * } * @return array|IXR_Error Array contains (based on $fields parameter): * - 'post_id' * - 'post_title' * - 'post_date' * - 'post_date_gmt' * - 'post_modified' * - 'post_modified_gmt' * - 'post_status' * - 'post_type' * - 'post_name' * - 'post_author' * - 'post_password' * - 'post_excerpt' * - 'post_content' * - 'link' * - 'comment_status' * - 'ping_status' * - 'sticky' * - 'custom_fields' * - 'terms' * - 'categories' * - 'tags' * - 'enclosure' */ public function wp_getPost( $args ) { if ( ! $this->minimum_args( $args, 4 ) ) { return $this->error; } $this->escape( $args ); $username = $args[1]; $password = $args[2]; $post_id = (int) $args[3]; if ( isset( $args[4] ) ) { $fields = $args[4]; } else { /** * Filters the default post query fields used by the given XML-RPC method. * * @since 3.4.0 * * @param array $fields An array of post fields to retrieve. By default, * contains 'post', 'terms', and 'custom_fields'. * @param string $method Method name. */ $fields = apply_filters( 'xmlrpc_default_post_fields', array( 'post', 'terms', 'custom_fields' ), 'wp.getPost' ); } $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getPost', $args, $this ); $post = get_post( $post_id, ARRAY_A ); if ( empty( $post['ID'] ) ) { return new IXR_Error( 404, __( 'Invalid post ID.' ) ); } if ( ! current_user_can( 'edit_post', $post_id ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) ); } return $this->_prepare_post( $post, $fields ); } /** * Retrieves posts. * * @since 3.4.0 * * @see wp_get_recent_posts() * @see wp_getPost() for more on `$fields` * @see get_posts() for more on `$filter` values * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * @type array $3 Optional. Modifies the query used to retrieve posts. Accepts 'post_type', * 'post_status', 'number', 'offset', 'orderby', 's', and 'order'. * Default empty array. * @type array $4 Optional. The subset of post type fields to return in the response array. * } * @return array|IXR_Error Array containing a collection of posts. */ public function wp_getPosts( $args ) { if ( ! $this->minimum_args( $args, 3 ) ) { return $this->error; } $this->escape( $args ); $username = $args[1]; $password = $args[2]; $filter = isset( $args[3] ) ? $args[3] : array(); if ( isset( $args[4] ) ) { $fields = $args[4]; } else { /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ $fields = apply_filters( 'xmlrpc_default_post_fields', array( 'post', 'terms', 'custom_fields' ), 'wp.getPosts' ); } $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getPosts', $args, $this ); $query = array(); if ( isset( $filter['post_type'] ) ) { $post_type = get_post_type_object( $filter['post_type'] ); if ( ! ( (bool) $post_type ) ) { return new IXR_Error( 403, __( 'Invalid post type.' ) ); } } else { $post_type = get_post_type_object( 'post' ); } if ( ! current_user_can( $post_type->cap->edit_posts ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit posts in this post type.' ) ); } $query['post_type'] = $post_type->name; if ( isset( $filter['post_status'] ) ) { $query['post_status'] = $filter['post_status']; } if ( isset( $filter['number'] ) ) { $query['numberposts'] = absint( $filter['number'] ); } if ( isset( $filter['offset'] ) ) { $query['offset'] = absint( $filter['offset'] ); } if ( isset( $filter['orderby'] ) ) { $query['orderby'] = $filter['orderby']; if ( isset( $filter['order'] ) ) { $query['order'] = $filter['order']; } } if ( isset( $filter['s'] ) ) { $query['s'] = $filter['s']; } $posts_list = wp_get_recent_posts( $query ); if ( ! $posts_list ) { return array(); } // Holds all the posts data. $struct = array(); foreach ( $posts_list as $post ) { if ( ! current_user_can( 'edit_post', $post['ID'] ) ) { continue; } $struct[] = $this->_prepare_post( $post, $fields ); } return $struct; } /** * Creates a new term. * * @since 3.4.0 * * @see wp_insert_term() * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * @type array $3 Content struct for adding a new term. The struct must contain * the term 'name' and 'taxonomy'. Optional accepted values include * 'parent', 'description', and 'slug'. * } * @return int|IXR_Error The term ID on success, or an IXR_Error object on failure. */ public function wp_newTerm( $args ) { if ( ! $this->minimum_args( $args, 4 ) ) { return $this->error; } $this->escape( $args ); $username = $args[1]; $password = $args[2]; $content_struct = $args[3]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.newTerm', $args, $this ); if ( ! taxonomy_exists( $content_struct['taxonomy'] ) ) { return new IXR_Error( 403, __( 'Invalid taxonomy.' ) ); } $taxonomy = get_taxonomy( $content_struct['taxonomy'] ); if ( ! current_user_can( $taxonomy->cap->edit_terms ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to create terms in this taxonomy.' ) ); } $taxonomy = (array) $taxonomy; // Hold the data of the term. $term_data = array(); $term_data['name'] = trim( $content_struct['name'] ); if ( empty( $term_data['name'] ) ) { return new IXR_Error( 403, __( 'The term name cannot be empty.' ) ); } if ( isset( $content_struct['parent'] ) ) { if ( ! $taxonomy['hierarchical'] ) { return new IXR_Error( 403, __( 'This taxonomy is not hierarchical.' ) ); } $parent_term_id = (int) $content_struct['parent']; $parent_term = get_term( $parent_term_id, $taxonomy['name'] ); if ( is_wp_error( $parent_term ) ) { return new IXR_Error( 500, $parent_term->get_error_message() ); } if ( ! $parent_term ) { return new IXR_Error( 403, __( 'Parent term does not exist.' ) ); } $term_data['parent'] = $content_struct['parent']; } if ( isset( $content_struct['description'] ) ) { $term_data['description'] = $content_struct['description']; } if ( isset( $content_struct['slug'] ) ) { $term_data['slug'] = $content_struct['slug']; } $term = wp_insert_term( $term_data['name'], $taxonomy['name'], $term_data ); if ( is_wp_error( $term ) ) { return new IXR_Error( 500, $term->get_error_message() ); } if ( ! $term ) { return new IXR_Error( 500, __( 'Sorry, the term could not be created.' ) ); } // Add term meta. if ( isset( $content_struct['custom_fields'] ) ) { $this->set_term_custom_fields( $term['term_id'], $content_struct['custom_fields'] ); } return (string) $term['term_id']; } /** * Edits a term. * * @since 3.4.0 * * @see wp_update_term() * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * @type int $3 Term ID. * @type array $4 Content struct for editing a term. The struct must contain the * term 'taxonomy'. Optional accepted values include 'name', 'parent', * 'description', and 'slug'. * } * @return true|IXR_Error True on success, IXR_Error instance on failure. */ public function wp_editTerm( $args ) { if ( ! $this->minimum_args( $args, 5 ) ) { return $this->error; } $this->escape( $args ); $username = $args[1]; $password = $args[2]; $term_id = (int) $args[3]; $content_struct = $args[4]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.editTerm', $args, $this ); if ( ! taxonomy_exists( $content_struct['taxonomy'] ) ) { return new IXR_Error( 403, __( 'Invalid taxonomy.' ) ); } $taxonomy = get_taxonomy( $content_struct['taxonomy'] ); $taxonomy = (array) $taxonomy; // Hold the data of the term. $term_data = array(); $term = get_term( $term_id, $content_struct['taxonomy'] ); if ( is_wp_error( $term ) ) { return new IXR_Error( 500, $term->get_error_message() ); } if ( ! $term ) { return new IXR_Error( 404, __( 'Invalid term ID.' ) ); } if ( ! current_user_can( 'edit_term', $term_id ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this term.' ) ); } if ( isset( $content_struct['name'] ) ) { $term_data['name'] = trim( $content_struct['name'] ); if ( empty( $term_data['name'] ) ) { return new IXR_Error( 403, __( 'The term name cannot be empty.' ) ); } } if ( ! empty( $content_struct['parent'] ) ) { if ( ! $taxonomy['hierarchical'] ) { return new IXR_Error( 403, __( 'Cannot set parent term, taxonomy is not hierarchical.' ) ); } $parent_term_id = (int) $content_struct['parent']; $parent_term = get_term( $parent_term_id, $taxonomy['name'] ); if ( is_wp_error( $parent_term ) ) { return new IXR_Error( 500, $parent_term->get_error_message() ); } if ( ! $parent_term ) { return new IXR_Error( 403, __( 'Parent term does not exist.' ) ); } $term_data['parent'] = $content_struct['parent']; } if ( isset( $content_struct['description'] ) ) { $term_data['description'] = $content_struct['description']; } if ( isset( $content_struct['slug'] ) ) { $term_data['slug'] = $content_struct['slug']; } $term = wp_update_term( $term_id, $taxonomy['name'], $term_data ); if ( is_wp_error( $term ) ) { return new IXR_Error( 500, $term->get_error_message() ); } if ( ! $term ) { return new IXR_Error( 500, __( 'Sorry, editing the term failed.' ) ); } // Update term meta. if ( isset( $content_struct['custom_fields'] ) ) { $this->set_term_custom_fields( $term_id, $content_struct['custom_fields'] ); } return true; } /** * Deletes a term. * * @since 3.4.0 * * @see wp_delete_term() * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * @type string $3 Taxonomy name. * @type int $4 Term ID. * } * @return true|IXR_Error True on success, IXR_Error instance on failure. */ public function wp_deleteTerm( $args ) { if ( ! $this->minimum_args( $args, 5 ) ) { return $this->error; } $this->escape( $args ); $username = $args[1]; $password = $args[2]; $taxonomy = $args[3]; $term_id = (int) $args[4]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.deleteTerm', $args, $this ); if ( ! taxonomy_exists( $taxonomy ) ) { return new IXR_Error( 403, __( 'Invalid taxonomy.' ) ); } $taxonomy = get_taxonomy( $taxonomy ); $term = get_term( $term_id, $taxonomy->name ); if ( is_wp_error( $term ) ) { return new IXR_Error( 500, $term->get_error_message() ); } if ( ! $term ) { return new IXR_Error( 404, __( 'Invalid term ID.' ) ); } if ( ! current_user_can( 'delete_term', $term_id ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to delete this term.' ) ); } $result = wp_delete_term( $term_id, $taxonomy->name ); if ( is_wp_error( $result ) ) { return new IXR_Error( 500, $term->get_error_message() ); } if ( ! $result ) { return new IXR_Error( 500, __( 'Sorry, deleting the term failed.' ) ); } return $result; } /** * Retrieves a term. * * @since 3.4.0 * * @see get_term() * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * @type string $3 Taxonomy name. * @type int $4 Term ID. * } * @return array|IXR_Error IXR_Error on failure, array on success, containing: * - 'term_id' * - 'name' * - 'slug' * - 'term_group' * - 'term_taxonomy_id' * - 'taxonomy' * - 'description' * - 'parent' * - 'count' */ public function wp_getTerm( $args ) { if ( ! $this->minimum_args( $args, 5 ) ) { return $this->error; } $this->escape( $args ); $username = $args[1]; $password = $args[2]; $taxonomy = $args[3]; $term_id = (int) $args[4]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getTerm', $args, $this ); if ( ! taxonomy_exists( $taxonomy ) ) { return new IXR_Error( 403, __( 'Invalid taxonomy.' ) ); } $taxonomy = get_taxonomy( $taxonomy ); $term = get_term( $term_id, $taxonomy->name, ARRAY_A ); if ( is_wp_error( $term ) ) { return new IXR_Error( 500, $term->get_error_message() ); } if ( ! $term ) { return new IXR_Error( 404, __( 'Invalid term ID.' ) ); } if ( ! current_user_can( 'assign_term', $term_id ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to assign this term.' ) ); } return $this->_prepare_term( $term ); } /** * Retrieves all terms for a taxonomy. * * @since 3.4.0 * * The optional $filter parameter modifies the query used to retrieve terms. * Accepted keys are 'number', 'offset', 'orderby', 'order', 'hide_empty', and 'search'. * * @see get_terms() * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * @type string $3 Taxonomy name. * @type array $4 Optional. Modifies the query used to retrieve posts. Accepts 'number', * 'offset', 'orderby', 'order', 'hide_empty', and 'search'. Default empty array. * } * @return array|IXR_Error An associative array of terms data on success, IXR_Error instance otherwise. */ public function wp_getTerms( $args ) { if ( ! $this->minimum_args( $args, 4 ) ) { return $this->error; } $this->escape( $args ); $username = $args[1]; $password = $args[2]; $taxonomy = $args[3]; $filter = isset( $args[4] ) ? $args[4] : array(); $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getTerms', $args, $this ); if ( ! taxonomy_exists( $taxonomy ) ) { return new IXR_Error( 403, __( 'Invalid taxonomy.' ) ); } $taxonomy = get_taxonomy( $taxonomy ); if ( ! current_user_can( $taxonomy->cap->assign_terms ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to assign terms in this taxonomy.' ) ); } $query = array( 'taxonomy' => $taxonomy->name ); if ( isset( $filter['number'] ) ) { $query['number'] = absint( $filter['number'] ); } if ( isset( $filter['offset'] ) ) { $query['offset'] = absint( $filter['offset'] ); } if ( isset( $filter['orderby'] ) ) { $query['orderby'] = $filter['orderby']; if ( isset( $filter['order'] ) ) { $query['order'] = $filter['order']; } } if ( isset( $filter['hide_empty'] ) ) { $query['hide_empty'] = $filter['hide_empty']; } else { $query['get'] = 'all'; } if ( isset( $filter['search'] ) ) { $query['search'] = $filter['search']; } $terms = get_terms( $query ); if ( is_wp_error( $terms ) ) { return new IXR_Error( 500, $terms->get_error_message() ); } $struct = array(); foreach ( $terms as $term ) { $struct[] = $this->_prepare_term( $term ); } return $struct; } /** * Retrieves a taxonomy. * * @since 3.4.0 * * @see get_taxonomy() * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * @type string $3 Taxonomy name. * @type array $4 Optional. Array of taxonomy fields to limit to in the return. * Accepts 'labels', 'cap', 'menu', and 'object_type'. * Default empty array. * } * @return array|IXR_Error An array of taxonomy data on success, IXR_Error instance otherwise. */ public function wp_getTaxonomy( $args ) { if ( ! $this->minimum_args( $args, 4 ) ) { return $this->error; } $this->escape( $args ); $username = $args[1]; $password = $args[2]; $taxonomy = $args[3]; if ( isset( $args[4] ) ) { $fields = $args[4]; } else { /** * Filters the default taxonomy query fields used by the given XML-RPC method. * * @since 3.4.0 * * @param array $fields An array of taxonomy fields to retrieve. By default, * contains 'labels', 'cap', and 'object_type'. * @param string $method The method name. */ $fields = apply_filters( 'xmlrpc_default_taxonomy_fields', array( 'labels', 'cap', 'object_type' ), 'wp.getTaxonomy' ); } $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getTaxonomy', $args, $this ); if ( ! taxonomy_exists( $taxonomy ) ) { return new IXR_Error( 403, __( 'Invalid taxonomy.' ) ); } $taxonomy = get_taxonomy( $taxonomy ); if ( ! current_user_can( $taxonomy->cap->assign_terms ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to assign terms in this taxonomy.' ) ); } return $this->_prepare_taxonomy( $taxonomy, $fields ); } /** * Retrieves all taxonomies. * * @since 3.4.0 * * @see get_taxonomies() * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * @type array $3 Optional. An array of arguments for retrieving taxonomies. * @type array $4 Optional. The subset of taxonomy fields to return. * } * @return array|IXR_Error An associative array of taxonomy data with returned fields determined * by `$fields`, or an IXR_Error instance on failure. */ public function wp_getTaxonomies( $args ) { if ( ! $this->minimum_args( $args, 3 ) ) { return $this->error; } $this->escape( $args ); $username = $args[1]; $password = $args[2]; $filter = isset( $args[3] ) ? $args[3] : array( 'public' => true ); if ( isset( $args[4] ) ) { $fields = $args[4]; } else { /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ $fields = apply_filters( 'xmlrpc_default_taxonomy_fields', array( 'labels', 'cap', 'object_type' ), 'wp.getTaxonomies' ); } $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getTaxonomies', $args, $this ); $taxonomies = get_taxonomies( $filter, 'objects' ); // Holds all the taxonomy data. $struct = array(); foreach ( $taxonomies as $taxonomy ) { // Capability check for post types. if ( ! current_user_can( $taxonomy->cap->assign_terms ) ) { continue; } $struct[] = $this->_prepare_taxonomy( $taxonomy, $fields ); } return $struct; } /** * Retrieves a user. * * The optional $fields parameter specifies what fields will be included * in the response array. This should be a list of field names. 'user_id' will * always be included in the response regardless of the value of $fields. * * Instead of, or in addition to, individual field names, conceptual group * names can be used to specify multiple fields. The available conceptual * groups are 'basic' and 'all'. * * @uses get_userdata() * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * @type int $3 User ID. * @type array $4 Optional. Array of fields to return. * } * @return array|IXR_Error Array contains (based on $fields parameter): * - 'user_id' * - 'username' * - 'first_name' * - 'last_name' * - 'registered' * - 'bio' * - 'email' * - 'nickname' * - 'nicename' * - 'url' * - 'display_name' * - 'roles' */ public function wp_getUser( $args ) { if ( ! $this->minimum_args( $args, 4 ) ) { return $this->error; } $this->escape( $args ); $username = $args[1]; $password = $args[2]; $user_id = (int) $args[3]; if ( isset( $args[4] ) ) { $fields = $args[4]; } else { /** * Filters the default user query fields used by the given XML-RPC method. * * @since 3.5.0 * * @param array $fields An array of user fields to retrieve. By default, contains 'all'. * @param string $method The method name. */ $fields = apply_filters( 'xmlrpc_default_user_fields', array( 'all' ), 'wp.getUser' ); } $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getUser', $args, $this ); if ( ! current_user_can( 'edit_user', $user_id ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this user.' ) ); } $user_data = get_userdata( $user_id ); if ( ! $user_data ) { return new IXR_Error( 404, __( 'Invalid user ID.' ) ); } return $this->_prepare_user( $user_data, $fields ); } /** * Retrieves users. * * The optional $filter parameter modifies the query used to retrieve users. * Accepted keys are 'number' (default: 50), 'offset' (default: 0), 'role', * 'who', 'orderby', and 'order'. * * The optional $fields parameter specifies what fields will be included * in the response array. * * @uses get_users() * @see wp_getUser() for more on $fields and return values * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * @type array $3 Optional. Arguments for the user query. * @type array $4 Optional. Fields to return. * } * @return array|IXR_Error users data */ public function wp_getUsers( $args ) { if ( ! $this->minimum_args( $args, 3 ) ) { return $this->error; } $this->escape( $args ); $username = $args[1]; $password = $args[2]; $filter = isset( $args[3] ) ? $args[3] : array(); if ( isset( $args[4] ) ) { $fields = $args[4]; } else { /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ $fields = apply_filters( 'xmlrpc_default_user_fields', array( 'all' ), 'wp.getUsers' ); } $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getUsers', $args, $this ); if ( ! current_user_can( 'list_users' ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to list users.' ) ); } $query = array( 'fields' => 'all_with_meta' ); $query['number'] = ( isset( $filter['number'] ) ) ? absint( $filter['number'] ) : 50; $query['offset'] = ( isset( $filter['offset'] ) ) ? absint( $filter['offset'] ) : 0; if ( isset( $filter['orderby'] ) ) { $query['orderby'] = $filter['orderby']; if ( isset( $filter['order'] ) ) { $query['order'] = $filter['order']; } } if ( isset( $filter['role'] ) ) { if ( get_role( $filter['role'] ) === null ) { return new IXR_Error( 403, __( 'Invalid role.' ) ); } $query['role'] = $filter['role']; } if ( isset( $filter['who'] ) ) { $query['who'] = $filter['who']; } $users = get_users( $query ); $_users = array(); foreach ( $users as $user_data ) { if ( current_user_can( 'edit_user', $user_data->ID ) ) { $_users[] = $this->_prepare_user( $user_data, $fields ); } } return $_users; } /** * Retrieves information about the requesting user. * * @uses get_userdata() * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username * @type string $2 Password * @type array $3 Optional. Fields to return. * } * @return array|IXR_Error (@see wp_getUser) */ public function wp_getProfile( $args ) { if ( ! $this->minimum_args( $args, 3 ) ) { return $this->error; } $this->escape( $args ); $username = $args[1]; $password = $args[2]; if ( isset( $args[3] ) ) { $fields = $args[3]; } else { /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ $fields = apply_filters( 'xmlrpc_default_user_fields', array( 'all' ), 'wp.getProfile' ); } $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getProfile', $args, $this ); if ( ! current_user_can( 'edit_user', $user->ID ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit your profile.' ) ); } $user_data = get_userdata( $user->ID ); return $this->_prepare_user( $user_data, $fields ); } /** * Edits user's profile. * * @uses wp_update_user() * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * @type array $3 Content struct. It can optionally contain: * - 'first_name' * - 'last_name' * - 'website' * - 'display_name' * - 'nickname' * - 'nicename' * - 'bio' * } * @return true|IXR_Error True, on success. */ public function wp_editProfile( $args ) { if ( ! $this->minimum_args( $args, 4 ) ) { return $this->error; } $this->escape( $args ); $username = $args[1]; $password = $args[2]; $content_struct = $args[3]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.editProfile', $args, $this ); if ( ! current_user_can( 'edit_user', $user->ID ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit your profile.' ) ); } // Holds data of the user. $user_data = array(); $user_data['ID'] = $user->ID; // Only set the user details if they were given. if ( isset( $content_struct['first_name'] ) ) { $user_data['first_name'] = $content_struct['first_name']; } if ( isset( $content_struct['last_name'] ) ) { $user_data['last_name'] = $content_struct['last_name']; } if ( isset( $content_struct['url'] ) ) { $user_data['user_url'] = $content_struct['url']; } if ( isset( $content_struct['display_name'] ) ) { $user_data['display_name'] = $content_struct['display_name']; } if ( isset( $content_struct['nickname'] ) ) { $user_data['nickname'] = $content_struct['nickname']; } if ( isset( $content_struct['nicename'] ) ) { $user_data['user_nicename'] = $content_struct['nicename']; } if ( isset( $content_struct['bio'] ) ) { $user_data['description'] = $content_struct['bio']; } $result = wp_update_user( $user_data ); if ( is_wp_error( $result ) ) { return new IXR_Error( 500, $result->get_error_message() ); } if ( ! $result ) { return new IXR_Error( 500, __( 'Sorry, the user could not be updated.' ) ); } return true; } /** * Retrieves a page. * * @since 2.2.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type int $1 Page ID. * @type string $2 Username. * @type string $3 Password. * } * @return array|IXR_Error */ public function wp_getPage( $args ) { $this->escape( $args ); $page_id = (int) $args[1]; $username = $args[2]; $password = $args[3]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } $page = get_post( $page_id ); if ( ! $page ) { return new IXR_Error( 404, __( 'Invalid post ID.' ) ); } if ( ! current_user_can( 'edit_page', $page_id ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this page.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getPage', $args, $this ); // If we found the page then format the data. if ( $page->ID && ( 'page' === $page->post_type ) ) { return $this->_prepare_page( $page ); } else { // If the page doesn't exist, indicate that. return new IXR_Error( 404, __( 'Sorry, no such page.' ) ); } } /** * Retrieves Pages. * * @since 2.2.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * @type int $3 Optional. Number of pages. Default 10. * } * @return array|IXR_Error */ public function wp_getPages( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $num_pages = isset( $args[3] ) ? (int) $args[3] : 10; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! current_user_can( 'edit_pages' ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit pages.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getPages', $args, $this ); $pages = get_posts( array( 'post_type' => 'page', 'post_status' => 'any', 'numberposts' => $num_pages, ) ); $num_pages = count( $pages ); // If we have pages, put together their info. if ( $num_pages >= 1 ) { $pages_struct = array(); foreach ( $pages as $page ) { if ( current_user_can( 'edit_page', $page->ID ) ) { $pages_struct[] = $this->_prepare_page( $page ); } } return $pages_struct; } return array(); } /** * Creates a new page. * * @since 2.2.0 * * @see wp_xmlrpc_server::mw_newPost() * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * @type array $3 Content struct. * } * @return int|IXR_Error */ public function wp_newPage( $args ) { // Items not escaped here will be escaped in wp_newPost(). $username = $this->escape( $args[1] ); $password = $this->escape( $args[2] ); $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.newPage', $args, $this ); // Mark this as content for a page. $args[3]['post_type'] = 'page'; // Let mw_newPost() do all of the heavy lifting. return $this->mw_newPost( $args ); } /** * Deletes a page. * * @since 2.2.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * @type int $3 Page ID. * } * @return true|IXR_Error True, if success. */ public function wp_deletePage( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $page_id = (int) $args[3]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.deletePage', $args, $this ); /* * Get the current page based on the 'page_id' and * make sure it is a page and not a post. */ $actual_page = get_post( $page_id, ARRAY_A ); if ( ! $actual_page || ( 'page' !== $actual_page['post_type'] ) ) { return new IXR_Error( 404, __( 'Sorry, no such page.' ) ); } // Make sure the user can delete pages. if ( ! current_user_can( 'delete_page', $page_id ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to delete this page.' ) ); } // Attempt to delete the page. $result = wp_delete_post( $page_id ); if ( ! $result ) { return new IXR_Error( 500, __( 'Failed to delete the page.' ) ); } /** * Fires after a page has been successfully deleted via XML-RPC. * * @since 3.4.0 * * @param int $page_id ID of the deleted page. * @param array $args An array of arguments to delete the page. */ do_action( 'xmlrpc_call_success_wp_deletePage', $page_id, $args ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase return true; } /** * Edits a page. * * @since 2.2.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type int $1 Page ID. * @type string $2 Username. * @type string $3 Password. * @type string $4 Content. * @type int $5 Publish flag. 0 for draft, 1 for publish. * } * @return array|IXR_Error */ public function wp_editPage( $args ) { // Items will be escaped in mw_editPost(). $page_id = (int) $args[1]; $username = $args[2]; $password = $args[3]; $content = $args[4]; $publish = $args[5]; $escaped_username = $this->escape( $username ); $escaped_password = $this->escape( $password ); $user = $this->login( $escaped_username, $escaped_password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.editPage', $args, $this ); // Get the page data and make sure it is a page. $actual_page = get_post( $page_id, ARRAY_A ); if ( ! $actual_page || ( 'page' !== $actual_page['post_type'] ) ) { return new IXR_Error( 404, __( 'Sorry, no such page.' ) ); } // Make sure the user is allowed to edit pages. if ( ! current_user_can( 'edit_page', $page_id ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this page.' ) ); } // Mark this as content for a page. $content['post_type'] = 'page'; // Arrange args in the way mw_editPost() understands. $args = array( $page_id, $username, $password, $content, $publish, ); // Let mw_editPost() do all of the heavy lifting. return $this->mw_editPost( $args ); } /** * Retrieves page list. * * @since 2.2.0 * * @global wpdb $wpdb WordPress database abstraction object. * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * } * @return array|IXR_Error */ public function wp_getPageList( $args ) { global $wpdb; $this->escape( $args ); $username = $args[1]; $password = $args[2]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! current_user_can( 'edit_pages' ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit pages.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getPageList', $args, $this ); // Get list of page IDs and titles. $page_list = $wpdb->get_results( " SELECT ID page_id, post_title page_title, post_parent page_parent_id, post_date_gmt, post_date, post_status FROM {$wpdb->posts} WHERE post_type = 'page' ORDER BY ID " ); // The date needs to be formatted properly. $num_pages = count( $page_list ); for ( $i = 0; $i < $num_pages; $i++ ) { $page_list[ $i ]->dateCreated = $this->_convert_date( $page_list[ $i ]->post_date ); $page_list[ $i ]->date_created_gmt = $this->_convert_date_gmt( $page_list[ $i ]->post_date_gmt, $page_list[ $i ]->post_date ); unset( $page_list[ $i ]->post_date_gmt ); unset( $page_list[ $i ]->post_date ); unset( $page_list[ $i ]->post_status ); } return $page_list; } /** * Retrieves authors list. * * @since 2.2.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * } * @return array|IXR_Error */ public function wp_getAuthors( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! current_user_can( 'edit_posts' ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit posts.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getAuthors', $args, $this ); $authors = array(); foreach ( get_users( array( 'fields' => array( 'ID', 'user_login', 'display_name' ) ) ) as $user ) { $authors[] = array( 'user_id' => $user->ID, 'user_login' => $user->user_login, 'display_name' => $user->display_name, ); } return $authors; } /** * Gets the list of all tags. * * @since 2.7.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * } * @return array|IXR_Error */ public function wp_getTags( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! current_user_can( 'edit_posts' ) ) { return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view tags.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getKeywords', $args, $this ); $tags = array(); $all_tags = get_tags(); if ( $all_tags ) { foreach ( (array) $all_tags as $tag ) { $struct = array(); $struct['tag_id'] = $tag->term_id; $struct['name'] = $tag->name; $struct['count'] = $tag->count; $struct['slug'] = $tag->slug; $struct['html_url'] = esc_html( get_tag_link( $tag->term_id ) ); $struct['rss_url'] = esc_html( get_tag_feed_link( $tag->term_id ) ); $tags[] = $struct; } } return $tags; } /** * Creates a new category. * * @since 2.2.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * @type array $3 Category. * } * @return int|IXR_Error Category ID. */ public function wp_newCategory( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $category = $args[3]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.newCategory', $args, $this ); // Make sure the user is allowed to add a category. if ( ! current_user_can( 'manage_categories' ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to add a category.' ) ); } /* * If no slug was provided, make it empty * so that WordPress will generate one. */ if ( empty( $category['slug'] ) ) { $category['slug'] = ''; } /* * If no parent_id was provided, make it empty * so that it will be a top-level page (no parent). */ if ( ! isset( $category['parent_id'] ) ) { $category['parent_id'] = ''; } // If no description was provided, make it empty. if ( empty( $category['description'] ) ) { $category['description'] = ''; } $new_category = array( 'cat_name' => $category['name'], 'category_nicename' => $category['slug'], 'category_parent' => $category['parent_id'], 'category_description' => $category['description'], ); $cat_id = wp_insert_category( $new_category, true ); if ( is_wp_error( $cat_id ) ) { if ( 'term_exists' === $cat_id->get_error_code() ) { return (int) $cat_id->get_error_data(); } else { return new IXR_Error( 500, __( 'Sorry, the category could not be created.' ) ); } } elseif ( ! $cat_id ) { return new IXR_Error( 500, __( 'Sorry, the category could not be created.' ) ); } /** * Fires after a new category has been successfully created via XML-RPC. * * @since 3.4.0 * * @param int $cat_id ID of the new category. * @param array $args An array of new category arguments. */ do_action( 'xmlrpc_call_success_wp_newCategory', $cat_id, $args ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase return $cat_id; } /** * Deletes a category. * * @since 2.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * @type int $3 Category ID. * } * @return bool|IXR_Error See wp_delete_term() for return info. */ public function wp_deleteCategory( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $category_id = (int) $args[3]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.deleteCategory', $args, $this ); if ( ! current_user_can( 'delete_term', $category_id ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to delete this category.' ) ); } $status = wp_delete_term( $category_id, 'category' ); if ( true == $status ) { /** * Fires after a category has been successfully deleted via XML-RPC. * * @since 3.4.0 * * @param int $category_id ID of the deleted category. * @param array $args An array of arguments to delete the category. */ do_action( 'xmlrpc_call_success_wp_deleteCategory', $category_id, $args ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase } return $status; } /** * Retrieves category list. * * @since 2.2.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * @type array $3 Category * @type int $4 Max number of results. * } * @return array|IXR_Error */ public function wp_suggestCategories( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $category = $args[3]; $max_results = (int) $args[4]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! current_user_can( 'edit_posts' ) ) { return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view categories.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.suggestCategories', $args, $this ); $category_suggestions = array(); $args = array( 'get' => 'all', 'number' => $max_results, 'name__like' => $category, ); foreach ( (array) get_categories( $args ) as $cat ) { $category_suggestions[] = array( 'category_id' => $cat->term_id, 'category_name' => $cat->name, ); } return $category_suggestions; } /** * Retrieves a comment. * * @since 2.7.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * @type int $3 Comment ID. * } * @return array|IXR_Error */ public function wp_getComment( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $comment_id = (int) $args[3]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getComment', $args, $this ); $comment = get_comment( $comment_id ); if ( ! $comment ) { return new IXR_Error( 404, __( 'Invalid comment ID.' ) ); } if ( ! current_user_can( 'edit_comment', $comment_id ) ) { return new IXR_Error( 403, __( 'Sorry, you are not allowed to moderate or edit this comment.' ) ); } return $this->_prepare_comment( $comment ); } /** * Retrieves comments. * * Besides the common blog_id (unused), username, and password arguments, * it takes a filter array as the last argument. * * Accepted 'filter' keys are 'status', 'post_id', 'offset', and 'number'. * * The defaults are as follows: * - 'status' - Default is ''. Filter by status (e.g., 'approve', 'hold') * - 'post_id' - Default is ''. The post where the comment is posted. * Empty string shows all comments. * - 'number' - Default is 10. Total number of media items to retrieve. * - 'offset' - Default is 0. See WP_Query::query() for more. * * @since 2.7.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * @type array $3 Optional. Query arguments. * } * @return array|IXR_Error Array containing a collection of comments. * See wp_xmlrpc_server::wp_getComment() for a description * of each item contents. */ public function wp_getComments( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $struct = isset( $args[3] ) ? $args[3] : array(); $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getComments', $args, $this ); if ( isset( $struct['status'] ) ) { $status = $struct['status']; } else { $status = ''; } if ( ! current_user_can( 'moderate_comments' ) && 'approve' !== $status ) { return new IXR_Error( 401, __( 'Invalid comment status.' ) ); } $post_id = ''; if ( isset( $struct['post_id'] ) ) { $post_id = absint( $struct['post_id'] ); } $post_type = ''; if ( isset( $struct['post_type'] ) ) { $post_type_object = get_post_type_object( $struct['post_type'] ); if ( ! $post_type_object || ! post_type_supports( $post_type_object->name, 'comments' ) ) { return new IXR_Error( 404, __( 'Invalid post type.' ) ); } $post_type = $struct['post_type']; } $offset = 0; if ( isset( $struct['offset'] ) ) { $offset = absint( $struct['offset'] ); } $number = 10; if ( isset( $struct['number'] ) ) { $number = absint( $struct['number'] ); } $comments = get_comments( array( 'status' => $status, 'post_id' => $post_id, 'offset' => $offset, 'number' => $number, 'post_type' => $post_type, ) ); $comments_struct = array(); if ( is_array( $comments ) ) { foreach ( $comments as $comment ) { $comments_struct[] = $this->_prepare_comment( $comment ); } } return $comments_struct; } /** * Deletes a comment. * * By default, the comment will be moved to the Trash instead of deleted. * See wp_delete_comment() for more information on this behavior. * * @since 2.7.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * @type int $3 Comment ID. * } * @return bool|IXR_Error See wp_delete_comment(). */ public function wp_deleteComment( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $comment_id = (int) $args[3]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! get_comment( $comment_id ) ) { return new IXR_Error( 404, __( 'Invalid comment ID.' ) ); } if ( ! current_user_can( 'edit_comment', $comment_id ) ) { return new IXR_Error( 403, __( 'Sorry, you are not allowed to delete this comment.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.deleteComment', $args, $this ); $status = wp_delete_comment( $comment_id ); if ( $status ) { /** * Fires after a comment has been successfully deleted via XML-RPC. * * @since 3.4.0 * * @param int $comment_id ID of the deleted comment. * @param array $args An array of arguments to delete the comment. */ do_action( 'xmlrpc_call_success_wp_deleteComment', $comment_id, $args ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase } return $status; } /** * Edits a comment. * * Besides the common blog_id (unused), username, and password arguments, * it takes a comment_id integer and a content_struct array as the last argument. * * The allowed keys in the content_struct array are: * - 'author' * - 'author_url' * - 'author_email' * - 'content' * - 'date_created_gmt' * - 'status'. Common statuses are 'approve', 'hold', 'spam'. See get_comment_statuses() for more details. * * @since 2.7.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * @type int $3 Comment ID. * @type array $4 Content structure. * } * @return true|IXR_Error True, on success. */ public function wp_editComment( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $comment_id = (int) $args[3]; $content_struct = $args[4]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! get_comment( $comment_id ) ) { return new IXR_Error( 404, __( 'Invalid comment ID.' ) ); } if ( ! current_user_can( 'edit_comment', $comment_id ) ) { return new IXR_Error( 403, __( 'Sorry, you are not allowed to moderate or edit this comment.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.editComment', $args, $this ); $comment = array( 'comment_ID' => $comment_id, ); if ( isset( $content_struct['status'] ) ) { $statuses = get_comment_statuses(); $statuses = array_keys( $statuses ); if ( ! in_array( $content_struct['status'], $statuses, true ) ) { return new IXR_Error( 401, __( 'Invalid comment status.' ) ); } $comment['comment_approved'] = $content_struct['status']; } // Do some timestamp voodoo. if ( ! empty( $content_struct['date_created_gmt'] ) ) { // We know this is supposed to be GMT, so we're going to slap that Z on there by force. $dateCreated = rtrim( $content_struct['date_created_gmt']->getIso(), 'Z' ) . 'Z'; $comment['comment_date'] = get_date_from_gmt( $dateCreated ); $comment['comment_date_gmt'] = iso8601_to_datetime( $dateCreated, 'gmt' ); } if ( isset( $content_struct['content'] ) ) { $comment['comment_content'] = $content_struct['content']; } if ( isset( $content_struct['author'] ) ) { $comment['comment_author'] = $content_struct['author']; } if ( isset( $content_struct['author_url'] ) ) { $comment['comment_author_url'] = $content_struct['author_url']; } if ( isset( $content_struct['author_email'] ) ) { $comment['comment_author_email'] = $content_struct['author_email']; } $result = wp_update_comment( $comment, true ); if ( is_wp_error( $result ) ) { return new IXR_Error( 500, $result->get_error_message() ); } if ( ! $result ) { return new IXR_Error( 500, __( 'Sorry, the comment could not be updated.' ) ); } /** * Fires after a comment has been successfully updated via XML-RPC. * * @since 3.4.0 * * @param int $comment_id ID of the updated comment. * @param array $args An array of arguments to update the comment. */ do_action( 'xmlrpc_call_success_wp_editComment', $comment_id, $args ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase return true; } /** * Creates a new comment. * * @since 2.7.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * @type string|int $3 Post ID or URL. * @type array $4 Content structure. * } * @return int|IXR_Error See wp_new_comment(). */ public function wp_newComment( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $post = $args[3]; $content_struct = $args[4]; /** * Filters whether to allow anonymous comments over XML-RPC. * * @since 2.7.0 * * @param bool $allow Whether to allow anonymous commenting via XML-RPC. * Default false. */ $allow_anon = apply_filters( 'xmlrpc_allow_anonymous_comments', false ); $user = $this->login( $username, $password ); if ( ! $user ) { $logged_in = false; if ( $allow_anon && get_option( 'comment_registration' ) ) { return new IXR_Error( 403, __( 'Sorry, you must be logged in to comment.' ) ); } elseif ( ! $allow_anon ) { return $this->error; } } else { $logged_in = true; } if ( is_numeric( $post ) ) { $post_id = absint( $post ); } else { $post_id = url_to_postid( $post ); } if ( ! $post_id ) { return new IXR_Error( 404, __( 'Invalid post ID.' ) ); } if ( ! get_post( $post_id ) ) { return new IXR_Error( 404, __( 'Invalid post ID.' ) ); } if ( ! comments_open( $post_id ) ) { return new IXR_Error( 403, __( 'Sorry, comments are closed for this item.' ) ); } if ( 'publish' === get_post_status( $post_id ) && ! current_user_can( 'edit_post', $post_id ) && post_password_required( $post_id ) ) { return new IXR_Error( 403, __( 'Sorry, you are not allowed to comment on this post.' ) ); } if ( 'private' === get_post_status( $post_id ) && ! current_user_can( 'read_post', $post_id ) ) { return new IXR_Error( 403, __( 'Sorry, you are not allowed to comment on this post.' ) ); } $comment = array( 'comment_post_ID' => $post_id, 'comment_content' => trim( $content_struct['content'] ), ); if ( $logged_in ) { $display_name = $user->display_name; $user_email = $user->user_email; $user_url = $user->user_url; $comment['comment_author'] = $this->escape( $display_name ); $comment['comment_author_email'] = $this->escape( $user_email ); $comment['comment_author_url'] = $this->escape( $user_url ); $comment['user_id'] = $user->ID; } else { $comment['comment_author'] = ''; if ( isset( $content_struct['author'] ) ) { $comment['comment_author'] = $content_struct['author']; } $comment['comment_author_email'] = ''; if ( isset( $content_struct['author_email'] ) ) { $comment['comment_author_email'] = $content_struct['author_email']; } $comment['comment_author_url'] = ''; if ( isset( $content_struct['author_url'] ) ) { $comment['comment_author_url'] = $content_struct['author_url']; } $comment['user_id'] = 0; if ( get_option( 'require_name_email' ) ) { if ( strlen( $comment['comment_author_email'] ) < 6 || '' === $comment['comment_author'] ) { return new IXR_Error( 403, __( 'Comment author name and email are required.' ) ); } elseif ( ! is_email( $comment['comment_author_email'] ) ) { return new IXR_Error( 403, __( 'A valid email address is required.' ) ); } } } $comment['comment_parent'] = isset( $content_struct['comment_parent'] ) ? absint( $content_struct['comment_parent'] ) : 0; /** This filter is documented in wp-includes/comment.php */ $allow_empty = apply_filters( 'allow_empty_comment', false, $comment ); if ( ! $allow_empty && '' === $comment['comment_content'] ) { return new IXR_Error( 403, __( 'Comment is required.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.newComment', $args, $this ); $comment_id = wp_new_comment( $comment, true ); if ( is_wp_error( $comment_id ) ) { return new IXR_Error( 403, $comment_id->get_error_message() ); } if ( ! $comment_id ) { return new IXR_Error( 403, __( 'Something went wrong.' ) ); } /** * Fires after a new comment has been successfully created via XML-RPC. * * @since 3.4.0 * * @param int $comment_id ID of the new comment. * @param array $args An array of new comment arguments. */ do_action( 'xmlrpc_call_success_wp_newComment', $comment_id, $args ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase return $comment_id; } /** * Retrieves all of the comment status. * * @since 2.7.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * } * @return array|IXR_Error */ public function wp_getCommentStatusList( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! current_user_can( 'publish_posts' ) ) { return new IXR_Error( 403, __( 'Sorry, you are not allowed to access details about this site.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getCommentStatusList', $args, $this ); return get_comment_statuses(); } /** * Retrieves comment counts. * * @since 2.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * @type int $3 Post ID. * } * @return array|IXR_Error */ public function wp_getCommentCount( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $post_id = (int) $args[3]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } $post = get_post( $post_id, ARRAY_A ); if ( empty( $post['ID'] ) ) { return new IXR_Error( 404, __( 'Invalid post ID.' ) ); } if ( ! current_user_can( 'edit_post', $post_id ) ) { return new IXR_Error( 403, __( 'Sorry, you are not allowed to access details of this post.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getCommentCount', $args, $this ); $count = wp_count_comments( $post_id ); return array( 'approved' => $count->approved, 'awaiting_moderation' => $count->moderated, 'spam' => $count->spam, 'total_comments' => $count->total_comments, ); } /** * Retrieves post statuses. * * @since 2.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * } * @return array|IXR_Error */ public function wp_getPostStatusList( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! current_user_can( 'edit_posts' ) ) { return new IXR_Error( 403, __( 'Sorry, you are not allowed to access details about this site.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getPostStatusList', $args, $this ); return get_post_statuses(); } /** * Retrieves page statuses. * * @since 2.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * } * @return array|IXR_Error */ public function wp_getPageStatusList( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! current_user_can( 'edit_pages' ) ) { return new IXR_Error( 403, __( 'Sorry, you are not allowed to access details about this site.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getPageStatusList', $args, $this ); return get_page_statuses(); } /** * Retrieves page templates. * * @since 2.6.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * } * @return array|IXR_Error */ public function wp_getPageTemplates( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! current_user_can( 'edit_pages' ) ) { return new IXR_Error( 403, __( 'Sorry, you are not allowed to access details about this site.' ) ); } $templates = get_page_templates(); $templates['Default'] = 'default'; return $templates; } /** * Retrieves blog options. * * @since 2.6.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * @type array $3 Optional. Options. * } * @return array|IXR_Error */ public function wp_getOptions( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $options = isset( $args[3] ) ? (array) $args[3] : array(); $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } // If no specific options where asked for, return all of them. if ( count( $options ) === 0 ) { $options = array_keys( $this->blog_options ); } return $this->_getOptions( $options ); } /** * Retrieves blog options value from list. * * @since 2.6.0 * * @param array $options Options to retrieve. * @return array */ public function _getOptions( $options ) { $data = array(); $can_manage = current_user_can( 'manage_options' ); foreach ( $options as $option ) { if ( array_key_exists( $option, $this->blog_options ) ) { $data[ $option ] = $this->blog_options[ $option ]; // Is the value static or dynamic? if ( isset( $data[ $option ]['option'] ) ) { $data[ $option ]['value'] = get_option( $data[ $option ]['option'] ); unset( $data[ $option ]['option'] ); } if ( ! $can_manage ) { $data[ $option ]['readonly'] = true; } } } return $data; } /** * Updates blog options. * * @since 2.6.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * @type array $3 Options. * } * @return array|IXR_Error */ public function wp_setOptions( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $options = (array) $args[3]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! current_user_can( 'manage_options' ) ) { return new IXR_Error( 403, __( 'Sorry, you are not allowed to update options.' ) ); } $option_names = array(); foreach ( $options as $o_name => $o_value ) { $option_names[] = $o_name; if ( ! array_key_exists( $o_name, $this->blog_options ) ) { continue; } if ( true == $this->blog_options[ $o_name ]['readonly'] ) { continue; } update_option( $this->blog_options[ $o_name ]['option'], wp_unslash( $o_value ) ); } // Now return the updated values. return $this->_getOptions( $option_names ); } /** * Retrieves a media item by ID. * * @since 3.1.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * @type int $3 Attachment ID. * } * @return array|IXR_Error Associative array contains: * - 'date_created_gmt' * - 'parent' * - 'link' * - 'thumbnail' * - 'title' * - 'caption' * - 'description' * - 'metadata' */ public function wp_getMediaItem( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $attachment_id = (int) $args[3]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! current_user_can( 'upload_files' ) ) { return new IXR_Error( 403, __( 'Sorry, you are not allowed to upload files.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getMediaItem', $args, $this ); $attachment = get_post( $attachment_id ); if ( ! $attachment || 'attachment' !== $attachment->post_type ) { return new IXR_Error( 404, __( 'Invalid attachment ID.' ) ); } return $this->_prepare_media_item( $attachment ); } /** * Retrieves a collection of media library items (or attachments). * * Besides the common blog_id (unused), username, and password arguments, * it takes a filter array as the last argument. * * Accepted 'filter' keys are 'parent_id', 'mime_type', 'offset', and 'number'. * * The defaults are as follows: * - 'number' - Default is 5. Total number of media items to retrieve. * - 'offset' - Default is 0. See WP_Query::query() for more. * - 'parent_id' - Default is ''. The post where the media item is attached. * Empty string shows all media items. 0 shows unattached media items. * - 'mime_type' - Default is ''. Filter by mime type (e.g., 'image/jpeg', 'application/pdf') * * @since 3.1.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * @type array $3 Optional. Query arguments. * } * @return array|IXR_Error Array containing a collection of media items. * See wp_xmlrpc_server::wp_getMediaItem() for a description * of each item contents. */ public function wp_getMediaLibrary( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $struct = isset( $args[3] ) ? $args[3] : array(); $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! current_user_can( 'upload_files' ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to upload files.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getMediaLibrary', $args, $this ); $parent_id = ( isset( $struct['parent_id'] ) ) ? absint( $struct['parent_id'] ) : ''; $mime_type = ( isset( $struct['mime_type'] ) ) ? $struct['mime_type'] : ''; $offset = ( isset( $struct['offset'] ) ) ? absint( $struct['offset'] ) : 0; $number = ( isset( $struct['number'] ) ) ? absint( $struct['number'] ) : -1; $attachments = get_posts( array( 'post_type' => 'attachment', 'post_parent' => $parent_id, 'offset' => $offset, 'numberposts' => $number, 'post_mime_type' => $mime_type, ) ); $attachments_struct = array(); foreach ( $attachments as $attachment ) { $attachments_struct[] = $this->_prepare_media_item( $attachment ); } return $attachments_struct; } /** * Retrieves a list of post formats used by the site. * * @since 3.1.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * } * @return array|IXR_Error List of post formats, otherwise IXR_Error object. */ public function wp_getPostFormats( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! current_user_can( 'edit_posts' ) ) { return new IXR_Error( 403, __( 'Sorry, you are not allowed to access details about this site.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getPostFormats', $args, $this ); $formats = get_post_format_strings(); // Find out if they want a list of currently supports formats. if ( isset( $args[3] ) && is_array( $args[3] ) ) { if ( $args[3]['show-supported'] ) { if ( current_theme_supports( 'post-formats' ) ) { $supported = get_theme_support( 'post-formats' ); $data = array(); $data['all'] = $formats; $data['supported'] = $supported[0]; $formats = $data; } } } return $formats; } /** * Retrieves a post type. * * @since 3.4.0 * * @see get_post_type_object() * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * @type string $3 Post type name. * @type array $4 Optional. Fields to fetch. * } * @return array|IXR_Error Array contains: * - 'labels' * - 'description' * - 'capability_type' * - 'cap' * - 'map_meta_cap' * - 'hierarchical' * - 'menu_position' * - 'taxonomies' * - 'supports' */ public function wp_getPostType( $args ) { if ( ! $this->minimum_args( $args, 4 ) ) { return $this->error; } $this->escape( $args ); $username = $args[1]; $password = $args[2]; $post_type_name = $args[3]; if ( isset( $args[4] ) ) { $fields = $args[4]; } else { /** * Filters the default post type query fields used by the given XML-RPC method. * * @since 3.4.0 * * @param array $fields An array of post type fields to retrieve. By default, * contains 'labels', 'cap', and 'taxonomies'. * @param string $method The method name. */ $fields = apply_filters( 'xmlrpc_default_posttype_fields', array( 'labels', 'cap', 'taxonomies' ), 'wp.getPostType' ); } $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getPostType', $args, $this ); if ( ! post_type_exists( $post_type_name ) ) { return new IXR_Error( 403, __( 'Invalid post type.' ) ); } $post_type = get_post_type_object( $post_type_name ); if ( ! current_user_can( $post_type->cap->edit_posts ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit posts in this post type.' ) ); } return $this->_prepare_post_type( $post_type, $fields ); } /** * Retrieves post types. * * @since 3.4.0 * * @see get_post_types() * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * @type array $3 Optional. Query arguments. * @type array $4 Optional. Fields to fetch. * } * @return array|IXR_Error */ public function wp_getPostTypes( $args ) { if ( ! $this->minimum_args( $args, 3 ) ) { return $this->error; } $this->escape( $args ); $username = $args[1]; $password = $args[2]; $filter = isset( $args[3] ) ? $args[3] : array( 'public' => true ); if ( isset( $args[4] ) ) { $fields = $args[4]; } else { /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ $fields = apply_filters( 'xmlrpc_default_posttype_fields', array( 'labels', 'cap', 'taxonomies' ), 'wp.getPostTypes' ); } $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getPostTypes', $args, $this ); $post_types = get_post_types( $filter, 'objects' ); $struct = array(); foreach ( $post_types as $post_type ) { if ( ! current_user_can( $post_type->cap->edit_posts ) ) { continue; } $struct[ $post_type->name ] = $this->_prepare_post_type( $post_type, $fields ); } return $struct; } /** * Retrieves revisions for a specific post. * * @since 3.5.0 * * The optional $fields parameter specifies what fields will be included * in the response array. * * @uses wp_get_post_revisions() * @see wp_getPost() for more on $fields * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * @type int $3 Post ID. * @type array $4 Optional. Fields to fetch. * } * @return array|IXR_Error Array containing a collection of posts. */ public function wp_getRevisions( $args ) { if ( ! $this->minimum_args( $args, 4 ) ) { return $this->error; } $this->escape( $args ); $username = $args[1]; $password = $args[2]; $post_id = (int) $args[3]; if ( isset( $args[4] ) ) { $fields = $args[4]; } else { /** * Filters the default revision query fields used by the given XML-RPC method. * * @since 3.5.0 * * @param array $field An array of revision fields to retrieve. By default, * contains 'post_date' and 'post_date_gmt'. * @param string $method The method name. */ $fields = apply_filters( 'xmlrpc_default_revision_fields', array( 'post_date', 'post_date_gmt' ), 'wp.getRevisions' ); } $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.getRevisions', $args, $this ); $post = get_post( $post_id ); if ( ! $post ) { return new IXR_Error( 404, __( 'Invalid post ID.' ) ); } if ( ! current_user_can( 'edit_post', $post_id ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit posts.' ) ); } // Check if revisions are enabled. if ( ! wp_revisions_enabled( $post ) ) { return new IXR_Error( 401, __( 'Sorry, revisions are disabled.' ) ); } $revisions = wp_get_post_revisions( $post_id ); if ( ! $revisions ) { return array(); } $struct = array(); foreach ( $revisions as $revision ) { if ( ! current_user_can( 'read_post', $revision->ID ) ) { continue; } // Skip autosaves. if ( wp_is_post_autosave( $revision ) ) { continue; } $struct[] = $this->_prepare_post( get_object_vars( $revision ), $fields ); } return $struct; } /** * Restores a post revision. * * @since 3.5.0 * * @uses wp_restore_post_revision() * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * @type int $3 Revision ID. * } * @return bool|IXR_Error false if there was an error restoring, true if success. */ public function wp_restoreRevision( $args ) { if ( ! $this->minimum_args( $args, 3 ) ) { return $this->error; } $this->escape( $args ); $username = $args[1]; $password = $args[2]; $revision_id = (int) $args[3]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'wp.restoreRevision', $args, $this ); $revision = wp_get_post_revision( $revision_id ); if ( ! $revision ) { return new IXR_Error( 404, __( 'Invalid post ID.' ) ); } if ( wp_is_post_autosave( $revision ) ) { return new IXR_Error( 404, __( 'Invalid post ID.' ) ); } $post = get_post( $revision->post_parent ); if ( ! $post ) { return new IXR_Error( 404, __( 'Invalid post ID.' ) ); } if ( ! current_user_can( 'edit_post', $revision->post_parent ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) ); } // Check if revisions are disabled. if ( ! wp_revisions_enabled( $post ) ) { return new IXR_Error( 401, __( 'Sorry, revisions are disabled.' ) ); } $post = wp_restore_post_revision( $revision_id ); return (bool) $post; } /* * Blogger API functions. * Specs on http://plant.blogger.com/api and https://groups.yahoo.com/group/bloggerDev/ */ /** * Retrieves blogs that user owns. * * Will make more sense once we support multiple blogs. * * @since 1.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * } * @return array|IXR_Error */ public function blogger_getUsersBlogs( $args ) { if ( ! $this->minimum_args( $args, 3 ) ) { return $this->error; } if ( is_multisite() ) { return $this->_multisite_getUsersBlogs( $args ); } $this->escape( $args ); $username = $args[1]; $password = $args[2]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'blogger.getUsersBlogs', $args, $this ); $is_admin = current_user_can( 'manage_options' ); $struct = array( 'isAdmin' => $is_admin, 'url' => get_option( 'home' ) . '/', 'blogid' => '1', 'blogName' => get_option( 'blogname' ), 'xmlrpc' => site_url( 'xmlrpc.php', 'rpc' ), ); return array( $struct ); } /** * Private function for retrieving a users blogs for multisite setups. * * @since 3.0.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * } * @return array|IXR_Error */ protected function _multisite_getUsersBlogs( $args ) { $current_blog = get_site(); $domain = $current_blog->domain; $path = $current_blog->path . 'xmlrpc.php'; $blogs = $this->wp_getUsersBlogs( $args ); if ( $blogs instanceof IXR_Error ) { return $blogs; } if ( $_SERVER['HTTP_HOST'] == $domain && $_SERVER['REQUEST_URI'] == $path ) { return $blogs; } else { foreach ( (array) $blogs as $blog ) { if ( str_contains( $blog['url'], $_SERVER['HTTP_HOST'] ) ) { return array( $blog ); } } return array(); } } /** * Retrieves user's data. * * Gives your client some info about you, so you don't have to. * * @since 1.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * } * @return array|IXR_Error */ public function blogger_getUserInfo( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! current_user_can( 'edit_posts' ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to access user data on this site.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'blogger.getUserInfo', $args, $this ); $struct = array( 'nickname' => $user->nickname, 'userid' => $user->ID, 'url' => $user->user_url, 'lastname' => $user->last_name, 'firstname' => $user->first_name, ); return $struct; } /** * Retrieves a post. * * @since 1.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type int $1 Post ID. * @type string $2 Username. * @type string $3 Password. * } * @return array|IXR_Error */ public function blogger_getPost( $args ) { $this->escape( $args ); $post_id = (int) $args[1]; $username = $args[2]; $password = $args[3]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } $post_data = get_post( $post_id, ARRAY_A ); if ( ! $post_data ) { return new IXR_Error( 404, __( 'Invalid post ID.' ) ); } if ( ! current_user_can( 'edit_post', $post_id ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'blogger.getPost', $args, $this ); $categories = implode( ',', wp_get_post_categories( $post_id ) ); $content = '<title>' . wp_unslash( $post_data['post_title'] ) . '</title>'; $content .= '<category>' . $categories . '</category>'; $content .= wp_unslash( $post_data['post_content'] ); $struct = array( 'userid' => $post_data['post_author'], 'dateCreated' => $this->_convert_date( $post_data['post_date'] ), 'content' => $content, 'postid' => (string) $post_data['ID'], ); return $struct; } /** * Retrieves the list of recent posts. * * @since 1.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type string $0 App key (unused). * @type int $1 Blog ID (unused). * @type string $2 Username. * @type string $3 Password. * @type int $4 Optional. Number of posts. * } * @return array|IXR_Error */ public function blogger_getRecentPosts( $args ) { $this->escape( $args ); // $args[0] = appkey - ignored. $username = $args[2]; $password = $args[3]; if ( isset( $args[4] ) ) { $query = array( 'numberposts' => absint( $args[4] ) ); } else { $query = array(); } $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! current_user_can( 'edit_posts' ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit posts.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'blogger.getRecentPosts', $args, $this ); $posts_list = wp_get_recent_posts( $query ); if ( ! $posts_list ) { $this->error = new IXR_Error( 500, __( 'Either there are no posts, or something went wrong.' ) ); return $this->error; } $recent_posts = array(); foreach ( $posts_list as $entry ) { if ( ! current_user_can( 'edit_post', $entry['ID'] ) ) { continue; } $post_date = $this->_convert_date( $entry['post_date'] ); $categories = implode( ',', wp_get_post_categories( $entry['ID'] ) ); $content = '<title>' . wp_unslash( $entry['post_title'] ) . '</title>'; $content .= '<category>' . $categories . '</category>'; $content .= wp_unslash( $entry['post_content'] ); $recent_posts[] = array( 'userid' => $entry['post_author'], 'dateCreated' => $post_date, 'content' => $content, 'postid' => (string) $entry['ID'], ); } return $recent_posts; } /** * Deprecated. * * @since 1.5.0 * @deprecated 3.5.0 * * @param array $args Unused. * @return IXR_Error Error object. */ public function blogger_getTemplate( $args ) { return new IXR_Error( 403, __( 'Sorry, this method is not supported.' ) ); } /** * Deprecated. * * @since 1.5.0 * @deprecated 3.5.0 * * @param array $args Unused. * @return IXR_Error Error object. */ public function blogger_setTemplate( $args ) { return new IXR_Error( 403, __( 'Sorry, this method is not supported.' ) ); } /** * Creates a new post. * * @since 1.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type string $0 App key (unused). * @type int $1 Blog ID (unused). * @type string $2 Username. * @type string $3 Password. * @type string $4 Content. * @type int $5 Publish flag. 0 for draft, 1 for publish. * } * @return int|IXR_Error */ public function blogger_newPost( $args ) { $this->escape( $args ); $username = $args[2]; $password = $args[3]; $content = $args[4]; $publish = $args[5]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'blogger.newPost', $args, $this ); $cap = ( $publish ) ? 'publish_posts' : 'edit_posts'; if ( ! current_user_can( get_post_type_object( 'post' )->cap->create_posts ) || ! current_user_can( $cap ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to post on this site.' ) ); } $post_status = ( $publish ) ? 'publish' : 'draft'; $post_author = $user->ID; $post_title = xmlrpc_getposttitle( $content ); $post_category = xmlrpc_getpostcategory( $content ); $post_content = xmlrpc_removepostdata( $content ); $post_date = current_time( 'mysql' ); $post_date_gmt = current_time( 'mysql', 1 ); $post_data = compact( 'post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_title', 'post_category', 'post_status' ); $post_id = wp_insert_post( $post_data ); if ( is_wp_error( $post_id ) ) { return new IXR_Error( 500, $post_id->get_error_message() ); } if ( ! $post_id ) { return new IXR_Error( 500, __( 'Sorry, the post could not be created.' ) ); } $this->attach_uploads( $post_id, $post_content ); /** * Fires after a new post has been successfully created via the XML-RPC Blogger API. * * @since 3.4.0 * * @param int $post_id ID of the new post. * @param array $args An array of new post arguments. */ do_action( 'xmlrpc_call_success_blogger_newPost', $post_id, $args ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase return $post_id; } /** * Edits a post. * * @since 1.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type int $1 Post ID. * @type string $2 Username. * @type string $3 Password. * @type string $4 Content * @type int $5 Publish flag. 0 for draft, 1 for publish. * } * @return true|IXR_Error true when done. */ public function blogger_editPost( $args ) { $this->escape( $args ); $post_id = (int) $args[1]; $username = $args[2]; $password = $args[3]; $content = $args[4]; $publish = $args[5]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'blogger.editPost', $args, $this ); $actual_post = get_post( $post_id, ARRAY_A ); if ( ! $actual_post || 'post' !== $actual_post['post_type'] ) { return new IXR_Error( 404, __( 'Sorry, no such post.' ) ); } $this->escape( $actual_post ); if ( ! current_user_can( 'edit_post', $post_id ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) ); } if ( 'publish' === $actual_post['post_status'] && ! current_user_can( 'publish_posts' ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to publish this post.' ) ); } $postdata = array(); $postdata['ID'] = $actual_post['ID']; $postdata['post_content'] = xmlrpc_removepostdata( $content ); $postdata['post_title'] = xmlrpc_getposttitle( $content ); $postdata['post_category'] = xmlrpc_getpostcategory( $content ); $postdata['post_status'] = $actual_post['post_status']; $postdata['post_excerpt'] = $actual_post['post_excerpt']; $postdata['post_status'] = $publish ? 'publish' : 'draft'; $result = wp_update_post( $postdata ); if ( ! $result ) { return new IXR_Error( 500, __( 'Sorry, the post could not be updated.' ) ); } $this->attach_uploads( $actual_post['ID'], $postdata['post_content'] ); /** * Fires after a post has been successfully updated via the XML-RPC Blogger API. * * @since 3.4.0 * * @param int $post_id ID of the updated post. * @param array $args An array of arguments for the post to edit. */ do_action( 'xmlrpc_call_success_blogger_editPost', $post_id, $args ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase return true; } /** * Deletes a post. * * @since 1.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type int $1 Post ID. * @type string $2 Username. * @type string $3 Password. * } * @return true|IXR_Error True when post is deleted. */ public function blogger_deletePost( $args ) { $this->escape( $args ); $post_id = (int) $args[1]; $username = $args[2]; $password = $args[3]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'blogger.deletePost', $args, $this ); $actual_post = get_post( $post_id, ARRAY_A ); if ( ! $actual_post || 'post' !== $actual_post['post_type'] ) { return new IXR_Error( 404, __( 'Sorry, no such post.' ) ); } if ( ! current_user_can( 'delete_post', $post_id ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to delete this post.' ) ); } $result = wp_delete_post( $post_id ); if ( ! $result ) { return new IXR_Error( 500, __( 'Sorry, the post could not be deleted.' ) ); } /** * Fires after a post has been successfully deleted via the XML-RPC Blogger API. * * @since 3.4.0 * * @param int $post_id ID of the deleted post. * @param array $args An array of arguments to delete the post. */ do_action( 'xmlrpc_call_success_blogger_deletePost', $post_id, $args ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase return true; } /* * MetaWeblog API functions. * Specs on wherever Dave Winer wants them to be. */ /** * Creates a new post. * * The 'content_struct' argument must contain: * - title * - description * - mt_excerpt * - mt_text_more * - mt_keywords * - mt_tb_ping_urls * - categories * * Also, it can optionally contain: * - wp_slug * - wp_password * - wp_page_parent_id * - wp_page_order * - wp_author_id * - post_status | page_status - can be 'draft', 'private', 'publish', or 'pending' * - mt_allow_comments - can be 'open' or 'closed' * - mt_allow_pings - can be 'open' or 'closed' * - date_created_gmt * - dateCreated * - wp_post_thumbnail * * @since 1.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * @type array $3 Content structure. * @type int $4 Optional. Publish flag. 0 for draft, 1 for publish. Default 0. * } * @return int|IXR_Error */ public function mw_newPost( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $content_struct = $args[3]; $publish = isset( $args[4] ) ? $args[4] : 0; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'metaWeblog.newPost', $args, $this ); $page_template = ''; if ( ! empty( $content_struct['post_type'] ) ) { if ( 'page' === $content_struct['post_type'] ) { if ( $publish ) { $cap = 'publish_pages'; } elseif ( isset( $content_struct['page_status'] ) && 'publish' === $content_struct['page_status'] ) { $cap = 'publish_pages'; } else { $cap = 'edit_pages'; } $error_message = __( 'Sorry, you are not allowed to publish pages on this site.' ); $post_type = 'page'; if ( ! empty( $content_struct['wp_page_template'] ) ) { $page_template = $content_struct['wp_page_template']; } } elseif ( 'post' === $content_struct['post_type'] ) { if ( $publish ) { $cap = 'publish_posts'; } elseif ( isset( $content_struct['post_status'] ) && 'publish' === $content_struct['post_status'] ) { $cap = 'publish_posts'; } else { $cap = 'edit_posts'; } $error_message = __( 'Sorry, you are not allowed to publish posts on this site.' ); $post_type = 'post'; } else { // No other 'post_type' values are allowed here. return new IXR_Error( 401, __( 'Invalid post type.' ) ); } } else { if ( $publish ) { $cap = 'publish_posts'; } elseif ( isset( $content_struct['post_status'] ) && 'publish' === $content_struct['post_status'] ) { $cap = 'publish_posts'; } else { $cap = 'edit_posts'; } $error_message = __( 'Sorry, you are not allowed to publish posts on this site.' ); $post_type = 'post'; } if ( ! current_user_can( get_post_type_object( $post_type )->cap->create_posts ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to publish posts on this site.' ) ); } if ( ! current_user_can( $cap ) ) { return new IXR_Error( 401, $error_message ); } // Check for a valid post format if one was given. if ( isset( $content_struct['wp_post_format'] ) ) { $content_struct['wp_post_format'] = sanitize_key( $content_struct['wp_post_format'] ); if ( ! array_key_exists( $content_struct['wp_post_format'], get_post_format_strings() ) ) { return new IXR_Error( 404, __( 'Invalid post format.' ) ); } } // Let WordPress generate the 'post_name' (slug) unless // one has been provided. $post_name = null; if ( isset( $content_struct['wp_slug'] ) ) { $post_name = $content_struct['wp_slug']; } // Only use a password if one was given. $post_password = ''; if ( isset( $content_struct['wp_password'] ) ) { $post_password = $content_struct['wp_password']; } // Only set a post parent if one was given. $post_parent = 0; if ( isset( $content_struct['wp_page_parent_id'] ) ) { $post_parent = $content_struct['wp_page_parent_id']; } // Only set the 'menu_order' if it was given. $menu_order = 0; if ( isset( $content_struct['wp_page_order'] ) ) { $menu_order = $content_struct['wp_page_order']; } $post_author = $user->ID; // If an author id was provided then use it instead. if ( isset( $content_struct['wp_author_id'] ) && ( $user->ID != $content_struct['wp_author_id'] ) ) { switch ( $post_type ) { case 'post': if ( ! current_user_can( 'edit_others_posts' ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to create posts as this user.' ) ); } break; case 'page': if ( ! current_user_can( 'edit_others_pages' ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to create pages as this user.' ) ); } break; default: return new IXR_Error( 401, __( 'Invalid post type.' ) ); } $author = get_userdata( $content_struct['wp_author_id'] ); if ( ! $author ) { return new IXR_Error( 404, __( 'Invalid author ID.' ) ); } $post_author = $content_struct['wp_author_id']; } $post_title = isset( $content_struct['title'] ) ? $content_struct['title'] : ''; $post_content = isset( $content_struct['description'] ) ? $content_struct['description'] : ''; $post_status = $publish ? 'publish' : 'draft'; if ( isset( $content_struct[ "{$post_type}_status" ] ) ) { switch ( $content_struct[ "{$post_type}_status" ] ) { case 'draft': case 'pending': case 'private': case 'publish': $post_status = $content_struct[ "{$post_type}_status" ]; break; default: // Deliberably left empty. break; } } $post_excerpt = isset( $content_struct['mt_excerpt'] ) ? $content_struct['mt_excerpt'] : ''; $post_more = isset( $content_struct['mt_text_more'] ) ? $content_struct['mt_text_more'] : ''; $tags_input = isset( $content_struct['mt_keywords'] ) ? $content_struct['mt_keywords'] : array(); if ( isset( $content_struct['mt_allow_comments'] ) ) { if ( ! is_numeric( $content_struct['mt_allow_comments'] ) ) { switch ( $content_struct['mt_allow_comments'] ) { case 'closed': $comment_status = 'closed'; break; case 'open': $comment_status = 'open'; break; default: $comment_status = get_default_comment_status( $post_type ); break; } } else { switch ( (int) $content_struct['mt_allow_comments'] ) { case 0: case 2: $comment_status = 'closed'; break; case 1: $comment_status = 'open'; break; default: $comment_status = get_default_comment_status( $post_type ); break; } } } else { $comment_status = get_default_comment_status( $post_type ); } if ( isset( $content_struct['mt_allow_pings'] ) ) { if ( ! is_numeric( $content_struct['mt_allow_pings'] ) ) { switch ( $content_struct['mt_allow_pings'] ) { case 'closed': $ping_status = 'closed'; break; case 'open': $ping_status = 'open'; break; default: $ping_status = get_default_comment_status( $post_type, 'pingback' ); break; } } else { switch ( (int) $content_struct['mt_allow_pings'] ) { case 0: $ping_status = 'closed'; break; case 1: $ping_status = 'open'; break; default: $ping_status = get_default_comment_status( $post_type, 'pingback' ); break; } } } else { $ping_status = get_default_comment_status( $post_type, 'pingback' ); } if ( $post_more ) { $post_content .= '<!--more-->' . $post_more; } $to_ping = ''; if ( isset( $content_struct['mt_tb_ping_urls'] ) ) { $to_ping = $content_struct['mt_tb_ping_urls']; if ( is_array( $to_ping ) ) { $to_ping = implode( ' ', $to_ping ); } } // Do some timestamp voodoo. if ( ! empty( $content_struct['date_created_gmt'] ) ) { // We know this is supposed to be GMT, so we're going to slap that Z on there by force. $dateCreated = rtrim( $content_struct['date_created_gmt']->getIso(), 'Z' ) . 'Z'; } elseif ( ! empty( $content_struct['dateCreated'] ) ) { $dateCreated = $content_struct['dateCreated']->getIso(); } $post_date = ''; $post_date_gmt = ''; if ( ! empty( $dateCreated ) ) { $post_date = iso8601_to_datetime( $dateCreated ); $post_date_gmt = iso8601_to_datetime( $dateCreated, 'gmt' ); } $post_category = array(); if ( isset( $content_struct['categories'] ) ) { $catnames = $content_struct['categories']; if ( is_array( $catnames ) ) { foreach ( $catnames as $cat ) { $post_category[] = get_cat_ID( $cat ); } } } $postdata = compact( 'post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt', 'comment_status', 'ping_status', 'to_ping', 'post_type', 'post_name', 'post_password', 'post_parent', 'menu_order', 'tags_input', 'page_template' ); $post_id = get_default_post_to_edit( $post_type, true )->ID; $postdata['ID'] = $post_id; // Only posts can be sticky. if ( 'post' === $post_type && isset( $content_struct['sticky'] ) ) { $data = $postdata; $data['sticky'] = $content_struct['sticky']; $error = $this->_toggle_sticky( $data ); if ( $error ) { return $error; } } if ( isset( $content_struct['custom_fields'] ) ) { $this->set_custom_fields( $post_id, $content_struct['custom_fields'] ); } if ( isset( $content_struct['wp_post_thumbnail'] ) ) { if ( set_post_thumbnail( $post_id, $content_struct['wp_post_thumbnail'] ) === false ) { return new IXR_Error( 404, __( 'Invalid attachment ID.' ) ); } unset( $content_struct['wp_post_thumbnail'] ); } // Handle enclosures. $thisEnclosure = isset( $content_struct['enclosure'] ) ? $content_struct['enclosure'] : null; $this->add_enclosure_if_new( $post_id, $thisEnclosure ); $this->attach_uploads( $post_id, $post_content ); /* * Handle post formats if assigned, value is validated earlier * in this function. */ if ( isset( $content_struct['wp_post_format'] ) ) { set_post_format( $post_id, $content_struct['wp_post_format'] ); } $post_id = wp_insert_post( $postdata, true ); if ( is_wp_error( $post_id ) ) { return new IXR_Error( 500, $post_id->get_error_message() ); } if ( ! $post_id ) { return new IXR_Error( 500, __( 'Sorry, the post could not be created.' ) ); } /** * Fires after a new post has been successfully created via the XML-RPC MovableType API. * * @since 3.4.0 * * @param int $post_id ID of the new post. * @param array $args An array of arguments to create the new post. */ do_action( 'xmlrpc_call_success_mw_newPost', $post_id, $args ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase return (string) $post_id; } /** * Adds an enclosure to a post if it's new. * * @since 2.8.0 * * @param int $post_id Post ID. * @param array $enclosure Enclosure data. */ public function add_enclosure_if_new( $post_id, $enclosure ) { if ( is_array( $enclosure ) && isset( $enclosure['url'] ) && isset( $enclosure['length'] ) && isset( $enclosure['type'] ) ) { $encstring = $enclosure['url'] . "\n" . $enclosure['length'] . "\n" . $enclosure['type'] . "\n"; $found = false; $enclosures = get_post_meta( $post_id, 'enclosure' ); if ( $enclosures ) { foreach ( $enclosures as $enc ) { // This method used to omit the trailing new line. #23219 if ( rtrim( $enc, "\n" ) === rtrim( $encstring, "\n" ) ) { $found = true; break; } } } if ( ! $found ) { add_post_meta( $post_id, 'enclosure', $encstring ); } } } /** * Attaches an upload to a post. * * @since 2.1.0 * * @global wpdb $wpdb WordPress database abstraction object. * * @param int $post_id Post ID. * @param string $post_content Post Content for attachment. */ public function attach_uploads( $post_id, $post_content ) { global $wpdb; // Find any unattached files. $attachments = $wpdb->get_results( "SELECT ID, guid FROM {$wpdb->posts} WHERE post_parent = '0' AND post_type = 'attachment'" ); if ( is_array( $attachments ) ) { foreach ( $attachments as $file ) { if ( ! empty( $file->guid ) && str_contains( $post_content, $file->guid ) ) { $wpdb->update( $wpdb->posts, array( 'post_parent' => $post_id ), array( 'ID' => $file->ID ) ); } } } } /** * Edits a post. * * @since 1.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Post ID. * @type string $1 Username. * @type string $2 Password. * @type array $3 Content structure. * @type int $4 Optional. Publish flag. 0 for draft, 1 for publish. Default 0. * } * @return true|IXR_Error True on success. */ public function mw_editPost( $args ) { $this->escape( $args ); $post_id = (int) $args[0]; $username = $args[1]; $password = $args[2]; $content_struct = $args[3]; $publish = isset( $args[4] ) ? $args[4] : 0; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'metaWeblog.editPost', $args, $this ); $postdata = get_post( $post_id, ARRAY_A ); /* * If there is no post data for the give post ID, stop now and return an error. * Otherwise a new post will be created (which was the old behavior). */ if ( ! $postdata || empty( $postdata['ID'] ) ) { return new IXR_Error( 404, __( 'Invalid post ID.' ) ); } if ( ! current_user_can( 'edit_post', $post_id ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) ); } // Use wp.editPost to edit post types other than post and page. if ( ! in_array( $postdata['post_type'], array( 'post', 'page' ), true ) ) { return new IXR_Error( 401, __( 'Invalid post type.' ) ); } // Thwart attempt to change the post type. if ( ! empty( $content_struct['post_type'] ) && ( $content_struct['post_type'] != $postdata['post_type'] ) ) { return new IXR_Error( 401, __( 'The post type may not be changed.' ) ); } // Check for a valid post format if one was given. if ( isset( $content_struct['wp_post_format'] ) ) { $content_struct['wp_post_format'] = sanitize_key( $content_struct['wp_post_format'] ); if ( ! array_key_exists( $content_struct['wp_post_format'], get_post_format_strings() ) ) { return new IXR_Error( 404, __( 'Invalid post format.' ) ); } } $this->escape( $postdata ); $ID = $postdata['ID']; $post_content = $postdata['post_content']; $post_title = $postdata['post_title']; $post_excerpt = $postdata['post_excerpt']; $post_password = $postdata['post_password']; $post_parent = $postdata['post_parent']; $post_type = $postdata['post_type']; $menu_order = $postdata['menu_order']; $ping_status = $postdata['ping_status']; $comment_status = $postdata['comment_status']; // Let WordPress manage slug if none was provided. $post_name = $postdata['post_name']; if ( isset( $content_struct['wp_slug'] ) ) { $post_name = $content_struct['wp_slug']; } // Only use a password if one was given. if ( isset( $content_struct['wp_password'] ) ) { $post_password = $content_struct['wp_password']; } // Only set a post parent if one was given. if ( isset( $content_struct['wp_page_parent_id'] ) ) { $post_parent = $content_struct['wp_page_parent_id']; } // Only set the 'menu_order' if it was given. if ( isset( $content_struct['wp_page_order'] ) ) { $menu_order = $content_struct['wp_page_order']; } $page_template = ''; if ( ! empty( $content_struct['wp_page_template'] ) && 'page' === $post_type ) { $page_template = $content_struct['wp_page_template']; } $post_author = $postdata['post_author']; // If an author id was provided then use it instead. if ( isset( $content_struct['wp_author_id'] ) ) { // Check permissions if attempting to switch author to or from another user. if ( $user->ID != $content_struct['wp_author_id'] || $user->ID != $post_author ) { switch ( $post_type ) { case 'post': if ( ! current_user_can( 'edit_others_posts' ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to change the post author as this user.' ) ); } break; case 'page': if ( ! current_user_can( 'edit_others_pages' ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to change the page author as this user.' ) ); } break; default: return new IXR_Error( 401, __( 'Invalid post type.' ) ); } $post_author = $content_struct['wp_author_id']; } } if ( isset( $content_struct['mt_allow_comments'] ) ) { if ( ! is_numeric( $content_struct['mt_allow_comments'] ) ) { switch ( $content_struct['mt_allow_comments'] ) { case 'closed': $comment_status = 'closed'; break; case 'open': $comment_status = 'open'; break; default: $comment_status = get_default_comment_status( $post_type ); break; } } else { switch ( (int) $content_struct['mt_allow_comments'] ) { case 0: case 2: $comment_status = 'closed'; break; case 1: $comment_status = 'open'; break; default: $comment_status = get_default_comment_status( $post_type ); break; } } } if ( isset( $content_struct['mt_allow_pings'] ) ) { if ( ! is_numeric( $content_struct['mt_allow_pings'] ) ) { switch ( $content_struct['mt_allow_pings'] ) { case 'closed': $ping_status = 'closed'; break; case 'open': $ping_status = 'open'; break; default: $ping_status = get_default_comment_status( $post_type, 'pingback' ); break; } } else { switch ( (int) $content_struct['mt_allow_pings'] ) { case 0: $ping_status = 'closed'; break; case 1: $ping_status = 'open'; break; default: $ping_status = get_default_comment_status( $post_type, 'pingback' ); break; } } } if ( isset( $content_struct['title'] ) ) { $post_title = $content_struct['title']; } if ( isset( $content_struct['description'] ) ) { $post_content = $content_struct['description']; } $post_category = array(); if ( isset( $content_struct['categories'] ) ) { $catnames = $content_struct['categories']; if ( is_array( $catnames ) ) { foreach ( $catnames as $cat ) { $post_category[] = get_cat_ID( $cat ); } } } if ( isset( $content_struct['mt_excerpt'] ) ) { $post_excerpt = $content_struct['mt_excerpt']; } $post_more = isset( $content_struct['mt_text_more'] ) ? $content_struct['mt_text_more'] : ''; $post_status = $publish ? 'publish' : 'draft'; if ( isset( $content_struct[ "{$post_type}_status" ] ) ) { switch ( $content_struct[ "{$post_type}_status" ] ) { case 'draft': case 'pending': case 'private': case 'publish': $post_status = $content_struct[ "{$post_type}_status" ]; break; default: $post_status = $publish ? 'publish' : 'draft'; break; } } $tags_input = isset( $content_struct['mt_keywords'] ) ? $content_struct['mt_keywords'] : array(); if ( 'publish' === $post_status || 'private' === $post_status ) { if ( 'page' === $post_type && ! current_user_can( 'publish_pages' ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to publish this page.' ) ); } elseif ( ! current_user_can( 'publish_posts' ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to publish this post.' ) ); } } if ( $post_more ) { $post_content = $post_content . '<!--more-->' . $post_more; } $to_ping = ''; if ( isset( $content_struct['mt_tb_ping_urls'] ) ) { $to_ping = $content_struct['mt_tb_ping_urls']; if ( is_array( $to_ping ) ) { $to_ping = implode( ' ', $to_ping ); } } // Do some timestamp voodoo. if ( ! empty( $content_struct['date_created_gmt'] ) ) { // We know this is supposed to be GMT, so we're going to slap that Z on there by force. $dateCreated = rtrim( $content_struct['date_created_gmt']->getIso(), 'Z' ) . 'Z'; } elseif ( ! empty( $content_struct['dateCreated'] ) ) { $dateCreated = $content_struct['dateCreated']->getIso(); } // Default to not flagging the post date to be edited unless it's intentional. $edit_date = false; if ( ! empty( $dateCreated ) ) { $post_date = iso8601_to_datetime( $dateCreated ); $post_date_gmt = iso8601_to_datetime( $dateCreated, 'gmt' ); // Flag the post date to be edited. $edit_date = true; } else { $post_date = $postdata['post_date']; $post_date_gmt = $postdata['post_date_gmt']; } // We've got all the data -- post it. $newpost = compact( 'ID', 'post_content', 'post_title', 'post_category', 'post_status', 'post_excerpt', 'comment_status', 'ping_status', 'edit_date', 'post_date', 'post_date_gmt', 'to_ping', 'post_name', 'post_password', 'post_parent', 'menu_order', 'post_author', 'tags_input', 'page_template' ); $result = wp_update_post( $newpost, true ); if ( is_wp_error( $result ) ) { return new IXR_Error( 500, $result->get_error_message() ); } if ( ! $result ) { return new IXR_Error( 500, __( 'Sorry, the post could not be updated.' ) ); } // Only posts can be sticky. if ( 'post' === $post_type && isset( $content_struct['sticky'] ) ) { $data = $newpost; $data['sticky'] = $content_struct['sticky']; $data['post_type'] = 'post'; $error = $this->_toggle_sticky( $data, true ); if ( $error ) { return $error; } } if ( isset( $content_struct['custom_fields'] ) ) { $this->set_custom_fields( $post_id, $content_struct['custom_fields'] ); } if ( isset( $content_struct['wp_post_thumbnail'] ) ) { // Empty value deletes, non-empty value adds/updates. if ( empty( $content_struct['wp_post_thumbnail'] ) ) { delete_post_thumbnail( $post_id ); } else { if ( set_post_thumbnail( $post_id, $content_struct['wp_post_thumbnail'] ) === false ) { return new IXR_Error( 404, __( 'Invalid attachment ID.' ) ); } } unset( $content_struct['wp_post_thumbnail'] ); } // Handle enclosures. $thisEnclosure = isset( $content_struct['enclosure'] ) ? $content_struct['enclosure'] : null; $this->add_enclosure_if_new( $post_id, $thisEnclosure ); $this->attach_uploads( $ID, $post_content ); // Handle post formats if assigned, validation is handled earlier in this function. if ( isset( $content_struct['wp_post_format'] ) ) { set_post_format( $post_id, $content_struct['wp_post_format'] ); } /** * Fires after a post has been successfully updated via the XML-RPC MovableType API. * * @since 3.4.0 * * @param int $post_id ID of the updated post. * @param array $args An array of arguments to update the post. */ do_action( 'xmlrpc_call_success_mw_editPost', $post_id, $args ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase return true; } /** * Retrieves a post. * * @since 1.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Post ID. * @type string $1 Username. * @type string $2 Password. * } * @return array|IXR_Error */ public function mw_getPost( $args ) { $this->escape( $args ); $post_id = (int) $args[0]; $username = $args[1]; $password = $args[2]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } $postdata = get_post( $post_id, ARRAY_A ); if ( ! $postdata ) { return new IXR_Error( 404, __( 'Invalid post ID.' ) ); } if ( ! current_user_can( 'edit_post', $post_id ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'metaWeblog.getPost', $args, $this ); if ( '' !== $postdata['post_date'] ) { $post_date = $this->_convert_date( $postdata['post_date'] ); $post_date_gmt = $this->_convert_date_gmt( $postdata['post_date_gmt'], $postdata['post_date'] ); $post_modified = $this->_convert_date( $postdata['post_modified'] ); $post_modified_gmt = $this->_convert_date_gmt( $postdata['post_modified_gmt'], $postdata['post_modified'] ); $categories = array(); $catids = wp_get_post_categories( $post_id ); foreach ( $catids as $catid ) { $categories[] = get_cat_name( $catid ); } $tagnames = array(); $tags = wp_get_post_tags( $post_id ); if ( ! empty( $tags ) ) { foreach ( $tags as $tag ) { $tagnames[] = $tag->name; } $tagnames = implode( ', ', $tagnames ); } else { $tagnames = ''; } $post = get_extended( $postdata['post_content'] ); $link = get_permalink( $postdata['ID'] ); // Get the author info. $author = get_userdata( $postdata['post_author'] ); $allow_comments = ( 'open' === $postdata['comment_status'] ) ? 1 : 0; $allow_pings = ( 'open' === $postdata['ping_status'] ) ? 1 : 0; // Consider future posts as published. if ( 'future' === $postdata['post_status'] ) { $postdata['post_status'] = 'publish'; } // Get post format. $post_format = get_post_format( $post_id ); if ( empty( $post_format ) ) { $post_format = 'standard'; } $sticky = false; if ( is_sticky( $post_id ) ) { $sticky = true; } $enclosure = array(); foreach ( (array) get_post_custom( $post_id ) as $key => $val ) { if ( 'enclosure' === $key ) { foreach ( (array) $val as $enc ) { $encdata = explode( "\n", $enc ); $enclosure['url'] = trim( htmlspecialchars( $encdata[0] ) ); $enclosure['length'] = (int) trim( $encdata[1] ); $enclosure['type'] = trim( $encdata[2] ); break 2; } } } $resp = array( 'dateCreated' => $post_date, 'userid' => $postdata['post_author'], 'postid' => $postdata['ID'], 'description' => $post['main'], 'title' => $postdata['post_title'], 'link' => $link, 'permaLink' => $link, // Commented out because no other tool seems to use this. // 'content' => $entry['post_content'], 'categories' => $categories, 'mt_excerpt' => $postdata['post_excerpt'], 'mt_text_more' => $post['extended'], 'wp_more_text' => $post['more_text'], 'mt_allow_comments' => $allow_comments, 'mt_allow_pings' => $allow_pings, 'mt_keywords' => $tagnames, 'wp_slug' => $postdata['post_name'], 'wp_password' => $postdata['post_password'], 'wp_author_id' => (string) $author->ID, 'wp_author_display_name' => $author->display_name, 'date_created_gmt' => $post_date_gmt, 'post_status' => $postdata['post_status'], 'custom_fields' => $this->get_custom_fields( $post_id ), 'wp_post_format' => $post_format, 'sticky' => $sticky, 'date_modified' => $post_modified, 'date_modified_gmt' => $post_modified_gmt, ); if ( ! empty( $enclosure ) ) { $resp['enclosure'] = $enclosure; } $resp['wp_post_thumbnail'] = get_post_thumbnail_id( $postdata['ID'] ); return $resp; } else { return new IXR_Error( 404, __( 'Sorry, no such post.' ) ); } } /** * Retrieves list of recent posts. * * @since 1.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * @type int $3 Optional. Number of posts. * } * @return array|IXR_Error */ public function mw_getRecentPosts( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; if ( isset( $args[3] ) ) { $query = array( 'numberposts' => absint( $args[3] ) ); } else { $query = array(); } $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! current_user_can( 'edit_posts' ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit posts.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'metaWeblog.getRecentPosts', $args, $this ); $posts_list = wp_get_recent_posts( $query ); if ( ! $posts_list ) { return array(); } $recent_posts = array(); foreach ( $posts_list as $entry ) { if ( ! current_user_can( 'edit_post', $entry['ID'] ) ) { continue; } $post_date = $this->_convert_date( $entry['post_date'] ); $post_date_gmt = $this->_convert_date_gmt( $entry['post_date_gmt'], $entry['post_date'] ); $post_modified = $this->_convert_date( $entry['post_modified'] ); $post_modified_gmt = $this->_convert_date_gmt( $entry['post_modified_gmt'], $entry['post_modified'] ); $categories = array(); $catids = wp_get_post_categories( $entry['ID'] ); foreach ( $catids as $catid ) { $categories[] = get_cat_name( $catid ); } $tagnames = array(); $tags = wp_get_post_tags( $entry['ID'] ); if ( ! empty( $tags ) ) { foreach ( $tags as $tag ) { $tagnames[] = $tag->name; } $tagnames = implode( ', ', $tagnames ); } else { $tagnames = ''; } $post = get_extended( $entry['post_content'] ); $link = get_permalink( $entry['ID'] ); // Get the post author info. $author = get_userdata( $entry['post_author'] ); $allow_comments = ( 'open' === $entry['comment_status'] ) ? 1 : 0; $allow_pings = ( 'open' === $entry['ping_status'] ) ? 1 : 0; // Consider future posts as published. if ( 'future' === $entry['post_status'] ) { $entry['post_status'] = 'publish'; } // Get post format. $post_format = get_post_format( $entry['ID'] ); if ( empty( $post_format ) ) { $post_format = 'standard'; } $recent_posts[] = array( 'dateCreated' => $post_date, 'userid' => $entry['post_author'], 'postid' => (string) $entry['ID'], 'description' => $post['main'], 'title' => $entry['post_title'], 'link' => $link, 'permaLink' => $link, // Commented out because no other tool seems to use this. // 'content' => $entry['post_content'], 'categories' => $categories, 'mt_excerpt' => $entry['post_excerpt'], 'mt_text_more' => $post['extended'], 'wp_more_text' => $post['more_text'], 'mt_allow_comments' => $allow_comments, 'mt_allow_pings' => $allow_pings, 'mt_keywords' => $tagnames, 'wp_slug' => $entry['post_name'], 'wp_password' => $entry['post_password'], 'wp_author_id' => (string) $author->ID, 'wp_author_display_name' => $author->display_name, 'date_created_gmt' => $post_date_gmt, 'post_status' => $entry['post_status'], 'custom_fields' => $this->get_custom_fields( $entry['ID'] ), 'wp_post_format' => $post_format, 'date_modified' => $post_modified, 'date_modified_gmt' => $post_modified_gmt, 'sticky' => ( 'post' === $entry['post_type'] && is_sticky( $entry['ID'] ) ), 'wp_post_thumbnail' => get_post_thumbnail_id( $entry['ID'] ), ); } return $recent_posts; } /** * Retrieves the list of categories on a given blog. * * @since 1.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * } * @return array|IXR_Error */ public function mw_getCategories( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! current_user_can( 'edit_posts' ) ) { return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view categories.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'metaWeblog.getCategories', $args, $this ); $categories_struct = array(); $cats = get_categories( array( 'get' => 'all' ) ); if ( $cats ) { foreach ( $cats as $cat ) { $struct = array(); $struct['categoryId'] = $cat->term_id; $struct['parentId'] = $cat->parent; $struct['description'] = $cat->name; $struct['categoryDescription'] = $cat->description; $struct['categoryName'] = $cat->name; $struct['htmlUrl'] = esc_html( get_category_link( $cat->term_id ) ); $struct['rssUrl'] = esc_html( get_category_feed_link( $cat->term_id, 'rss2' ) ); $categories_struct[] = $struct; } } return $categories_struct; } /** * Uploads a file, following your settings. * * Adapted from a patch by Johann Richard. * * @link http://mycvs.org/archives/2004/06/30/file-upload-to-wordpress-in-ecto/ * * @since 1.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * @type array $3 Data. * } * @return array|IXR_Error */ public function mw_newMediaObject( $args ) { $username = $this->escape( $args[1] ); $password = $this->escape( $args[2] ); $data = $args[3]; $name = sanitize_file_name( $data['name'] ); $type = $data['type']; $bits = $data['bits']; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'metaWeblog.newMediaObject', $args, $this ); if ( ! current_user_can( 'upload_files' ) ) { $this->error = new IXR_Error( 401, __( 'Sorry, you are not allowed to upload files.' ) ); return $this->error; } if ( is_multisite() && upload_is_user_over_quota( false ) ) { $this->error = new IXR_Error( 401, sprintf( /* translators: %s: Allowed space allocation. */ __( 'Sorry, you have used your space allocation of %s. Please delete some files to upload more files.' ), size_format( get_space_allowed() * MB_IN_BYTES ) ) ); return $this->error; } /** * Filters whether to preempt the XML-RPC media upload. * * Returning a truthy value will effectively short-circuit the media upload, * returning that value as a 500 error instead. * * @since 2.1.0 * * @param bool $error Whether to pre-empt the media upload. Default false. */ $upload_err = apply_filters( 'pre_upload_error', false ); if ( $upload_err ) { return new IXR_Error( 500, $upload_err ); } $upload = wp_upload_bits( $name, null, $bits ); if ( ! empty( $upload['error'] ) ) { /* translators: 1: File name, 2: Error message. */ $errorString = sprintf( __( 'Could not write file %1$s (%2$s).' ), $name, $upload['error'] ); return new IXR_Error( 500, $errorString ); } // Construct the attachment array. $post_id = 0; if ( ! empty( $data['post_id'] ) ) { $post_id = (int) $data['post_id']; if ( ! current_user_can( 'edit_post', $post_id ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) ); } } $attachment = array( 'post_title' => $name, 'post_content' => '', 'post_type' => 'attachment', 'post_parent' => $post_id, 'post_mime_type' => $type, 'guid' => $upload['url'], ); // Save the data. $id = wp_insert_attachment( $attachment, $upload['file'], $post_id ); wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $upload['file'] ) ); /** * Fires after a new attachment has been added via the XML-RPC MovableType API. * * @since 3.4.0 * * @param int $id ID of the new attachment. * @param array $args An array of arguments to add the attachment. */ do_action( 'xmlrpc_call_success_mw_newMediaObject', $id, $args ); // phpcs:ignore WordPress.NamingConventions.ValidHookName.NotLowercase $struct = $this->_prepare_media_item( get_post( $id ) ); // Deprecated values. $struct['id'] = $struct['attachment_id']; $struct['file'] = $struct['title']; $struct['url'] = $struct['link']; return $struct; } /* * MovableType API functions. * Specs archive on http://web.archive.org/web/20050220091302/http://www.movabletype.org:80/docs/mtmanual_programmatic.html */ /** * Retrieves the post titles of recent posts. * * @since 1.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * @type int $3 Optional. Number of posts. * } * @return array|IXR_Error */ public function mt_getRecentPostTitles( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; if ( isset( $args[3] ) ) { $query = array( 'numberposts' => absint( $args[3] ) ); } else { $query = array(); } $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'mt.getRecentPostTitles', $args, $this ); $posts_list = wp_get_recent_posts( $query ); if ( ! $posts_list ) { $this->error = new IXR_Error( 500, __( 'Either there are no posts, or something went wrong.' ) ); return $this->error; } $recent_posts = array(); foreach ( $posts_list as $entry ) { if ( ! current_user_can( 'edit_post', $entry['ID'] ) ) { continue; } $post_date = $this->_convert_date( $entry['post_date'] ); $post_date_gmt = $this->_convert_date_gmt( $entry['post_date_gmt'], $entry['post_date'] ); $recent_posts[] = array( 'dateCreated' => $post_date, 'userid' => $entry['post_author'], 'postid' => (string) $entry['ID'], 'title' => $entry['post_title'], 'post_status' => $entry['post_status'], 'date_created_gmt' => $post_date_gmt, ); } return $recent_posts; } /** * Retrieves the list of all categories on a blog. * * @since 1.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Blog ID (unused). * @type string $1 Username. * @type string $2 Password. * } * @return array|IXR_Error */ public function mt_getCategoryList( $args ) { $this->escape( $args ); $username = $args[1]; $password = $args[2]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! current_user_can( 'edit_posts' ) ) { return new IXR_Error( 401, __( 'Sorry, you must be able to edit posts on this site in order to view categories.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'mt.getCategoryList', $args, $this ); $categories_struct = array(); $cats = get_categories( array( 'hide_empty' => 0, 'hierarchical' => 0, ) ); if ( $cats ) { foreach ( $cats as $cat ) { $struct = array(); $struct['categoryId'] = $cat->term_id; $struct['categoryName'] = $cat->name; $categories_struct[] = $struct; } } return $categories_struct; } /** * Retrieves post categories. * * @since 1.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Post ID. * @type string $1 Username. * @type string $2 Password. * } * @return array|IXR_Error */ public function mt_getPostCategories( $args ) { $this->escape( $args ); $post_id = (int) $args[0]; $username = $args[1]; $password = $args[2]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } if ( ! get_post( $post_id ) ) { return new IXR_Error( 404, __( 'Invalid post ID.' ) ); } if ( ! current_user_can( 'edit_post', $post_id ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) ); } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'mt.getPostCategories', $args, $this ); $categories = array(); $catids = wp_get_post_categories( (int) $post_id ); // First listed category will be the primary category. $isPrimary = true; foreach ( $catids as $catid ) { $categories[] = array( 'categoryName' => get_cat_name( $catid ), 'categoryId' => (string) $catid, 'isPrimary' => $isPrimary, ); $isPrimary = false; } return $categories; } /** * Sets categories for a post. * * @since 1.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Post ID. * @type string $1 Username. * @type string $2 Password. * @type array $3 Categories. * } * @return true|IXR_Error True on success. */ public function mt_setPostCategories( $args ) { $this->escape( $args ); $post_id = (int) $args[0]; $username = $args[1]; $password = $args[2]; $categories = $args[3]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'mt.setPostCategories', $args, $this ); if ( ! get_post( $post_id ) ) { return new IXR_Error( 404, __( 'Invalid post ID.' ) ); } if ( ! current_user_can( 'edit_post', $post_id ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to edit this post.' ) ); } $catids = array(); foreach ( $categories as $cat ) { $catids[] = $cat['categoryId']; } wp_set_post_categories( $post_id, $catids ); return true; } /** * Retrieves an array of methods supported by this server. * * @since 1.5.0 * * @return array */ public function mt_supportedMethods() { /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'mt.supportedMethods', array(), $this ); return array_keys( $this->methods ); } /** * Retrieves an empty array because we don't support per-post text filters. * * @since 1.5.0 */ public function mt_supportedTextFilters() { /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'mt.supportedTextFilters', array(), $this ); /** * Filters the MoveableType text filters list for XML-RPC. * * @since 2.2.0 * * @param array $filters An array of text filters. */ return apply_filters( 'xmlrpc_text_filters', array() ); } /** * Retrieves trackbacks sent to a given post. * * @since 1.5.0 * * @global wpdb $wpdb WordPress database abstraction object. * * @param int $post_id * @return array|IXR_Error */ public function mt_getTrackbackPings( $post_id ) { global $wpdb; /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'mt.getTrackbackPings', $post_id, $this ); $actual_post = get_post( $post_id, ARRAY_A ); if ( ! $actual_post ) { return new IXR_Error( 404, __( 'Sorry, no such post.' ) ); } $comments = $wpdb->get_results( $wpdb->prepare( "SELECT comment_author_url, comment_content, comment_author_IP, comment_type FROM $wpdb->comments WHERE comment_post_ID = %d", $post_id ) ); if ( ! $comments ) { return array(); } $trackback_pings = array(); foreach ( $comments as $comment ) { if ( 'trackback' === $comment->comment_type ) { $content = $comment->comment_content; $title = substr( $content, 8, ( strpos( $content, '</strong>' ) - 8 ) ); $trackback_pings[] = array( 'pingTitle' => $title, 'pingURL' => $comment->comment_author_url, 'pingIP' => $comment->comment_author_IP, ); } } return $trackback_pings; } /** * Sets a post's publish status to 'publish'. * * @since 1.5.0 * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type int $0 Post ID. * @type string $1 Username. * @type string $2 Password. * } * @return int|IXR_Error */ public function mt_publishPost( $args ) { $this->escape( $args ); $post_id = (int) $args[0]; $username = $args[1]; $password = $args[2]; $user = $this->login( $username, $password ); if ( ! $user ) { return $this->error; } /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'mt.publishPost', $args, $this ); $postdata = get_post( $post_id, ARRAY_A ); if ( ! $postdata ) { return new IXR_Error( 404, __( 'Invalid post ID.' ) ); } if ( ! current_user_can( 'publish_posts' ) || ! current_user_can( 'edit_post', $post_id ) ) { return new IXR_Error( 401, __( 'Sorry, you are not allowed to publish this post.' ) ); } $postdata['post_status'] = 'publish'; // Retain old categories. $postdata['post_category'] = wp_get_post_categories( $post_id ); $this->escape( $postdata ); return wp_update_post( $postdata ); } /* * Pingback functions. * Specs on www.hixie.ch/specs/pingback/pingback */ /** * Retrieves a pingback and registers it. * * @since 1.5.0 * * @global wpdb $wpdb WordPress database abstraction object. * * @param array $args { * Method arguments. Note: arguments must be ordered as documented. * * @type string $0 URL of page linked from. * @type string $1 URL of page linked to. * } * @return string|IXR_Error */ public function pingback_ping( $args ) { global $wpdb; /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'pingback.ping', $args, $this ); $this->escape( $args ); $pagelinkedfrom = str_replace( '&', '&', $args[0] ); $pagelinkedto = str_replace( '&', '&', $args[1] ); $pagelinkedto = str_replace( '&', '&', $pagelinkedto ); /** * Filters the pingback source URI. * * @since 3.6.0 * * @param string $pagelinkedfrom URI of the page linked from. * @param string $pagelinkedto URI of the page linked to. */ $pagelinkedfrom = apply_filters( 'pingback_ping_source_uri', $pagelinkedfrom, $pagelinkedto ); if ( ! $pagelinkedfrom ) { return $this->pingback_error( 0, __( 'A valid URL was not provided.' ) ); } // Check if the page linked to is on our site. $pos1 = strpos( $pagelinkedto, str_replace( array( 'http://www.', 'http://', 'https://www.', 'https://' ), '', get_option( 'home' ) ) ); if ( ! $pos1 ) { return $this->pingback_error( 0, __( 'Is there no link to us?' ) ); } /* * Let's find which post is linked to. * FIXME: Does url_to_postid() cover all these cases already? * If so, then let's use it and drop the old code. */ $urltest = parse_url( $pagelinkedto ); $post_id = url_to_postid( $pagelinkedto ); if ( $post_id ) { // $way } elseif ( isset( $urltest['path'] ) && preg_match( '#p/[0-9]{1,}#', $urltest['path'], $match ) ) { // The path defines the post_ID (archives/p/XXXX). $blah = explode( '/', $match[0] ); $post_id = (int) $blah[1]; } elseif ( isset( $urltest['query'] ) && preg_match( '#p=[0-9]{1,}#', $urltest['query'], $match ) ) { // The query string defines the post_ID (?p=XXXX). $blah = explode( '=', $match[0] ); $post_id = (int) $blah[1]; } elseif ( isset( $urltest['fragment'] ) ) { // An #anchor is there, it's either... if ( (int) $urltest['fragment'] ) { // ...an integer #XXXX (simplest case), $post_id = (int) $urltest['fragment']; } elseif ( preg_match( '/post-[0-9]+/', $urltest['fragment'] ) ) { // ...a post ID in the form 'post-###', $post_id = preg_replace( '/[^0-9]+/', '', $urltest['fragment'] ); } elseif ( is_string( $urltest['fragment'] ) ) { // ...or a string #title, a little more complicated. $title = preg_replace( '/[^a-z0-9]/i', '.', $urltest['fragment'] ); $sql = $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_title RLIKE %s", $title ); $post_id = $wpdb->get_var( $sql ); if ( ! $post_id ) { // Returning unknown error '0' is better than die()'ing. return $this->pingback_error( 0, '' ); } } } else { // TODO: Attempt to extract a post ID from the given URL. return $this->pingback_error( 33, __( 'The specified target URL cannot be used as a target. It either does not exist, or it is not a pingback-enabled resource.' ) ); } $post_id = (int) $post_id; $post = get_post( $post_id ); if ( ! $post ) { // Post not found. return $this->pingback_error( 33, __( 'The specified target URL cannot be used as a target. It either does not exist, or it is not a pingback-enabled resource.' ) ); } if ( url_to_postid( $pagelinkedfrom ) == $post_id ) { return $this->pingback_error( 0, __( 'The source URL and the target URL cannot both point to the same resource.' ) ); } // Check if pings are on. if ( ! pings_open( $post ) ) { return $this->pingback_error( 33, __( 'The specified target URL cannot be used as a target. It either does not exist, or it is not a pingback-enabled resource.' ) ); } // Let's check that the remote site didn't already pingback this entry. if ( $wpdb->get_results( $wpdb->prepare( "SELECT * FROM $wpdb->comments WHERE comment_post_ID = %d AND comment_author_url = %s", $post_id, $pagelinkedfrom ) ) ) { return $this->pingback_error( 48, __( 'The pingback has already been registered.' ) ); } /* * The remote site may have sent the pingback before it finished publishing its own content * containing this pingback URL. If that happens then it won't be immediately possible to fetch * the pinging post; adding a small delay reduces the likelihood of this happening. * * While there are more robust methods than calling `sleep()` here (because `sleep()` merely * mitigates the risk of requesting the remote post before it's available), this is effective * enough for most cases and avoids introducing more complexity into this code. * * One way to improve the reliability of this code might be to add failure-handling to the remote * fetch and retry up to a set number of times if it receives a 404. This could also handle 401 and * 403 responses to differentiate the "does not exist" failure from the "may not access" failure. */ sleep( 1 ); $remote_ip = preg_replace( '/[^0-9a-fA-F:., ]/', '', $_SERVER['REMOTE_ADDR'] ); /** This filter is documented in wp-includes/class-wp-http.php */ $user_agent = apply_filters( 'http_headers_useragent', 'WordPress/' . get_bloginfo( 'version' ) . '; ' . get_bloginfo( 'url' ), $pagelinkedfrom ); // Let's check the remote site. $http_api_args = array( 'timeout' => 10, 'redirection' => 0, 'limit_response_size' => 153600, // 150 KB 'user-agent' => "$user_agent; verifying pingback from $remote_ip", 'headers' => array( 'X-Pingback-Forwarded-For' => $remote_ip, ), ); $request = wp_safe_remote_get( $pagelinkedfrom, $http_api_args ); $remote_source = wp_remote_retrieve_body( $request ); $remote_source_original = $remote_source; if ( ! $remote_source ) { return $this->pingback_error( 16, __( 'The source URL does not exist.' ) ); } /** * Filters the pingback remote source. * * @since 2.5.0 * * @param string $remote_source Response source for the page linked from. * @param string $pagelinkedto URL of the page linked to. */ $remote_source = apply_filters( 'pre_remote_source', $remote_source, $pagelinkedto ); // Work around bug in strip_tags(): $remote_source = str_replace( '<!DOC', '<DOC', $remote_source ); $remote_source = preg_replace( '/[\r\n\t ]+/', ' ', $remote_source ); // normalize spaces $remote_source = preg_replace( '/<\/*(h1|h2|h3|h4|h5|h6|p|th|td|li|dt|dd|pre|caption|input|textarea|button|body)[^>]*>/', "\n\n", $remote_source ); preg_match( '|<title>([^<]*?)</title>|is', $remote_source, $matchtitle ); $title = isset( $matchtitle[1] ) ? $matchtitle[1] : ''; if ( empty( $title ) ) { return $this->pingback_error( 32, __( 'A title on that page cannot be found.' ) ); } // Remove all script and style tags including their content. $remote_source = preg_replace( '@<(script|style)[^>]*?>.*?</\\1>@si', '', $remote_source ); // Just keep the tag we need. $remote_source = strip_tags( $remote_source, '<a>' ); $p = explode( "\n\n", $remote_source ); $preg_target = preg_quote( $pagelinkedto, '|' ); foreach ( $p as $para ) { if ( str_contains( $para, $pagelinkedto ) ) { // It exists, but is it a link? preg_match( '|<a[^>]+?' . $preg_target . '[^>]*>([^>]+?)</a>|', $para, $context ); // If the URL isn't in a link context, keep looking. if ( empty( $context ) ) { continue; } /* * We're going to use this fake tag to mark the context in a bit. * The marker is needed in case the link text appears more than once in the paragraph. */ $excerpt = preg_replace( '|\</?wpcontext\>|', '', $para ); // prevent really long link text if ( strlen( $context[1] ) > 100 ) { $context[1] = substr( $context[1], 0, 100 ) . '…'; } $marker = '<wpcontext>' . $context[1] . '</wpcontext>'; // Set up our marker. $excerpt = str_replace( $context[0], $marker, $excerpt ); // Swap out the link for our marker. $excerpt = strip_tags( $excerpt, '<wpcontext>' ); // Strip all tags but our context marker. $excerpt = trim( $excerpt ); $preg_marker = preg_quote( $marker, '|' ); $excerpt = preg_replace( "|.*?\s(.{0,100}$preg_marker.{0,100})\s.*|s", '$1', $excerpt ); $excerpt = strip_tags( $excerpt ); // YES, again, to remove the marker wrapper. break; } } if ( empty( $context ) ) { // Link to target not found. return $this->pingback_error( 17, __( 'The source URL does not contain a link to the target URL, and so cannot be used as a source.' ) ); } $pagelinkedfrom = str_replace( '&', '&', $pagelinkedfrom ); $context = '[…] ' . esc_html( $excerpt ) . ' […]'; $pagelinkedfrom = $this->escape( $pagelinkedfrom ); $comment_post_id = (int) $post_id; $comment_author = $title; $comment_author_email = ''; $this->escape( $comment_author ); $comment_author_url = $pagelinkedfrom; $comment_content = $context; $this->escape( $comment_content ); $comment_type = 'pingback'; $commentdata = array( 'comment_post_ID' => $comment_post_id, ); $commentdata += compact( 'comment_author', 'comment_author_url', 'comment_author_email', 'comment_content', 'comment_type', 'remote_source', 'remote_source_original' ); $comment_id = wp_new_comment( $commentdata ); if ( is_wp_error( $comment_id ) ) { return $this->pingback_error( 0, $comment_id->get_error_message() ); } /** * Fires after a post pingback has been sent. * * @since 0.71 * * @param int $comment_id Comment ID. */ do_action( 'pingback_post', $comment_id ); /* translators: 1: URL of the page linked from, 2: URL of the page linked to. */ return sprintf( __( 'Pingback from %1$s to %2$s registered. Keep the web talking! :-)' ), $pagelinkedfrom, $pagelinkedto ); } /** * Retrieves an array of URLs that pingbacked the given URL. * * Specs on http://www.aquarionics.com/misc/archives/blogite/0198.html * * @since 1.5.0 * * @global wpdb $wpdb WordPress database abstraction object. * * @param string $url * @return array|IXR_Error */ public function pingback_extensions_getPingbacks( $url ) { global $wpdb; /** This action is documented in wp-includes/class-wp-xmlrpc-server.php */ do_action( 'xmlrpc_call', 'pingback.extensions.getPingbacks', $url, $this ); $url = $this->escape( $url ); $post_id = url_to_postid( $url ); if ( ! $post_id ) { // We aren't sure that the resource is available and/or pingback enabled. return $this->pingback_error( 33, __( 'The specified target URL cannot be used as a target. It either does not exist, or it is not a pingback-enabled resource.' ) ); } $actual_post = get_post( $post_id, ARRAY_A ); if ( ! $actual_post ) { // No such post = resource not found. return $this->pingback_error( 32, __( 'The specified target URL does not exist.' ) ); } $comments = $wpdb->get_results( $wpdb->prepare( "SELECT comment_author_url, comment_content, comment_author_IP, comment_type FROM $wpdb->comments WHERE comment_post_ID = %d", $post_id ) ); if ( ! $comments ) { return array(); } $pingbacks = array(); foreach ( $comments as $comment ) { if ( 'pingback' === $comment->comment_type ) { $pingbacks[] = $comment->comment_author_url; } } return $pingbacks; } /** * Sends a pingback error based on the given error code and message. * * @since 3.6.0 * * @param int $code Error code. * @param string $message Error message. * @return IXR_Error Error object. */ protected function pingback_error( $code, $message ) { /** * Filters the XML-RPC pingback error return. * * @since 3.5.1 * * @param IXR_Error $error An IXR_Error object containing the error code and message. */ return apply_filters( 'xmlrpc_pingback_error', new IXR_Error( $code, $message ) ); } }
Close