12
+ * Instrumented test, which will execute on an Android device.
13
+ *
14
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
15
+ */
16
+@RunWith(AndroidJUnit4.class)
17
+public class ExampleInstrumentedTest {
18
+
19
+    @Test
20
+    public void useAppContext() throws Exception {
21
+        // Context of the app under test.
22
+        Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
23
+
24
+        assertEquals("com.getcapacitor.app", appContext.getPackageName());
25
+    }
26
+}

+ 42 - 0
android/app/src/main/AndroidManifest.xml

@@ -0,0 +1,42 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3
+    package="io.ionic.starter">
4
+
5
+    <application
6
+        android:allowBackup="true"
7
+        android:icon="@mipmap/ic_launcher"
8
+        android:label="@string/app_name"
9
+        android:roundIcon="@mipmap/ic_launcher_round"
10
+        android:supportsRtl="true"
11
+        android:theme="@style/AppTheme">
12
+
13
+        <activity
14
+            android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|smallestScreenSize|screenLayout|uiMode"
15
+            android:name="io.ionic.starter.MainActivity"
16
+            android:label="@string/title_activity_main"
17
+            android:theme="@style/AppTheme.NoActionBarLaunch"
18
+            android:launchMode="singleTask"
19
+            android:exported="true">
20
+
21
+            <intent-filter>
22
+                <action android:name="android.intent.action.MAIN" />
23
+                <category android:name="android.intent.category.LAUNCHER" />
24
+            </intent-filter>
25
+
26
+        </activity>
27
+
28
+        <provider
29
+            android:name="androidx.core.content.FileProvider"
30
+            android:authorities="${applicationId}.fileprovider"
31
+            android:exported="false"
32
+            android:grantUriPermissions="true">
33
+            <meta-data
34
+                android:name="android.support.FILE_PROVIDER_PATHS"
35
+                android:resource="@xml/file_paths"></meta-data>
36
+        </provider>
37
+    </application>
38
+
39
+    <!-- Permissions -->
40
+
41
+    <uses-permission android:name="android.permission.INTERNET" />
42
+</manifest>

+ 5 - 0
android/app/src/main/java/io/ionic/starter/MainActivity.java

@@ -0,0 +1,5 @@
1
+package io.ionic.starter;
2
+
3
+import com.getcapacitor.BridgeActivity;
4
+
5
+public class MainActivity extends BridgeActivity {}

BIN
android/app/src/main/res/drawable-land-hdpi/splash.png


BIN
android/app/src/main/res/drawable-land-mdpi/splash.png


BIN
android/app/src/main/res/drawable-land-xhdpi/splash.png


BIN
android/app/src/main/res/drawable-land-xxhdpi/splash.png


BIN
android/app/src/main/res/drawable-land-xxxhdpi/splash.png


BIN
android/app/src/main/res/drawable-port-hdpi/splash.png


BIN
android/app/src/main/res/drawable-port-mdpi/splash.png


BIN
android/app/src/main/res/drawable-port-xhdpi/splash.png


BIN
android/app/src/main/res/drawable-port-xxhdpi/splash.png


BIN
android/app/src/main/res/drawable-port-xxxhdpi/splash.png


Fichier diff supprimé car celui-ci est trop grand
+ 34 - 0
android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml


+ 170 - 0
android/app/src/main/res/drawable/ic_launcher_background.xml

@@ -0,0 +1,170 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
3
+    android:width="108dp"
4
+    android:height="108dp"
5
+    android:viewportHeight="108"
6
+    android:viewportWidth="108">
7
+    <path
8
+        android:fillColor="#26A69A"
9
+        android:pathData="M0,0h108v108h-108z" />
10
+    <path
11
+        android:fillColor="#00000000"
12
+        android:pathData="M9,0L9,108"
13
+        android:strokeColor="#33FFFFFF"
14
+        android:strokeWidth="0.8" />
15
+    <path
16
+        android:fillColor="#00000000"
17
+        android:pathData="M19,0L19,108"
18
+        android:strokeColor="#33FFFFFF"
19
+        android:strokeWidth="0.8" />
20
+    <path
21
+        android:fillColor="#00000000"
22
+        android:pathData="M29,0L29,108"
23
+        android:strokeColor="#33FFFFFF"
24
+        android:strokeWidth="0.8" />
25
+    <path
26
+        android:fillColor="#00000000"
27
+        android:pathData="M39,0L39,108"
28
+        android:strokeColor="#33FFFFFF"
29
+        android:strokeWidth="0.8" />
30
+    <path
31
+        android:fillColor="#00000000"
32
+        android:pathData="M49,0L49,108"
33
+        android:strokeColor="#33FFFFFF"
34
+        android:strokeWidth="0.8" />
35
+    <path
36
+        android:fillColor="#00000000"
37
+        android:pathData="M59,0L59,108"
38
+        android:strokeColor="#33FFFFFF"
39
+        android:strokeWidth="0.8" />
40
+    <path
41
+        android:fillColor="#00000000"
42
+        android:pathData="M69,0L69,108"
43
+        android:strokeColor="#33FFFFFF"
44
+        android:strokeWidth="0.8" />
45
+    <path
46
+        android:fillColor="#00000000"
47
+        android:pathData="M79,0L79,108"
48
+        android:strokeColor="#33FFFFFF"
49
+        android:strokeWidth="0.8" />
50
+    <path
51
+        android:fillColor="#00000000"
52
+        android:pathData="M89,0L89,108"
53
+        android:strokeColor="#33FFFFFF"
54
+        android:strokeWidth="0.8" />
55
+    <path
56
+        android:fillColor="#00000000"
57
+        android:pathData="M99,0L99,108"
58
+        android:strokeColor="#33FFFFFF"
59
+        android:strokeWidth="0.8" />
60
+    <path
61
+        android:fillColor="#00000000"
62
+        android:pathData="M0,9L108,9"
63
+        android:strokeColor="#33FFFFFF"
64
+        android:strokeWidth="0.8" />
65
+    <path
66
+        android:fillColor="#00000000"
67
+        android:pathData="M0,19L108,19"
68
+        android:strokeColor="#33FFFFFF"
69
+        android:strokeWidth="0.8" />
70
+    <path
71
+        android:fillColor="#00000000"
72
+        android:pathData="M0,29L108,29"
73
+        android:strokeColor="#33FFFFFF"
74
+        android:strokeWidth="0.8" />
75
+    <path
76
+        android:fillColor="#00000000"
77
+        android:pathData="M0,39L108,39"
78
+        android:strokeColor="#33FFFFFF"
79
+        android:strokeWidth="0.8" />
80
+    <path
81
+        android:fillColor="#00000000"
82
+        android:pathData="M0,49L108,49"
83
+        android:strokeColor="#33FFFFFF"
84
+        android:strokeWidth="0.8" />
85
+    <path
86
+        android:fillColor="#00000000"
87
+        android:pathData="M0,59L108,59"
88
+        android:strokeColor="#33FFFFFF"
89
+        android:strokeWidth="0.8" />
90
+    <path
91
+        android:fillColor="#00000000"
92
+        android:pathData="M0,69L108,69"
93
+        android:strokeColor="#33FFFFFF"
94
+        android:strokeWidth="0.8" />
95
+    <path
96
+        android:fillColor="#00000000"
97
+        android:pathData="M0,79L108,79"
98
+        android:strokeColor="#33FFFFFF"
99
+        android:strokeWidth="0.8" />
100
+    <path
101
+        android:fillColor="#00000000"
102
+        android:pathData="M0,89L108,89"
103
+        android:strokeColor="#33FFFFFF"
104
+        android:strokeWidth="0.8" />
105
+    <path
106
+        android:fillColor="#00000000"
107
+        android:pathData="M0,99L108,99"
108
+        android:strokeColor="#33FFFFFF"
109
+        android:strokeWidth="0.8" />
110
+    <path
111
+        android:fillColor="#00000000"
112
+        android:pathData="M19,29L89,29"
113
+        android:strokeColor="#33FFFFFF"
114
+        android:strokeWidth="0.8" />
115
+    <path
116
+        android:fillColor="#00000000"
117
+        android:pathData="M19,39L89,39"
118
+        android:strokeColor="#33FFFFFF"
119
+        android:strokeWidth="0.8" />
120
+    <path
121
+        android:fillColor="#00000000"
122
+        android:pathData="M19,49L89,49"
123
+        android:strokeColor="#33FFFFFF"
124
+        android:strokeWidth="0.8" />
125
+    <path
126
+        android:fillColor="#00000000"
127
+        android:pathData="M19,59L89,59"
128
+        android:strokeColor="#33FFFFFF"
129
+        android:strokeWidth="0.8" />
130
+    <path
131
+        android:fillColor="#00000000"
132
+        android:pathData="M19,69L89,69"
133
+        android:strokeColor="#33FFFFFF"
134
+        android:strokeWidth="0.8" />
135
+    <path
136
+        android:fillColor="#00000000"
137
+        android:pathData="M19,79L89,79"
138
+        android:strokeColor="#33FFFFFF"
139
+        android:strokeWidth="0.8" />
140
+    <path
141
+        android:fillColor="#00000000"
142
+        android:pathData="M29,19L29,89"
143
+        android:strokeColor="#33FFFFFF"
144
+        android:strokeWidth="0.8" />
145
+    <path
146
+        android:fillColor="#00000000"
147
+        android:pathData="M39,19L39,89"
148
+        android:strokeColor="#33FFFFFF"
149
+        android:strokeWidth="0.8" />
150
+    <path
151
+        android:fillColor="#00000000"
152
+        android:pathData="M49,19L49,89"
153
+        android:strokeColor="#33FFFFFF"
154
+        android:strokeWidth="0.8" />
155
+    <path
156
+        android:fillColor="#00000000"
157
+        android:pathData="M59,19L59,89"
158
+        android:strokeColor="#33FFFFFF"
159
+        android:strokeWidth="0.8" />
160
+    <path
161
+        android:fillColor="#00000000"
162
+        android:pathData="M69,19L69,89"
163
+        android:strokeColor="#33FFFFFF"
164
+        android:strokeWidth="0.8" />
165
+    <path
166
+        android:fillColor="#00000000"
167
+        android:pathData="M79,19L79,89"
168
+        android:strokeColor="#33FFFFFF"
169
+        android:strokeWidth="0.8" />
170
+</vector>

BIN
android/app/src/main/res/drawable/splash.png


+ 12 - 0
android/app/src/main/res/layout/activity_main.xml

@@ -0,0 +1,12 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
3
+    xmlns:app="http://schemas.android.com/apk/res-auto"
4
+    xmlns:tools="http://schemas.android.com/tools"
5
+    android:layout_width="match_parent"
6
+    android:layout_height="match_parent"
7
+    tools:context=".MainActivity">
8
+
9
+    <WebView
10
+        android:layout_width="match_parent"
11
+        android:layout_height="match_parent" />
12
+</androidx.coordinatorlayout.widget.CoordinatorLayout>

+ 5 - 0
android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml

@@ -0,0 +1,5 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
3
+    <background android:drawable="@color/ic_launcher_background"/>
4
+    <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
5
+</adaptive-icon>

+ 5 - 0
android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml

@@ -0,0 +1,5 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
3
+    <background android:drawable="@color/ic_launcher_background"/>
4
+    <foreground android:drawable="@mipmap/ic_launcher_foreground"/>
5
+</adaptive-icon>

BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher.png


BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png


BIN
android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png


BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher.png


BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png


BIN
android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png


BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher.png


BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png


BIN
android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png


BIN
android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png


BIN
android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png


BIN
android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png


BIN
android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png


BIN
android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png


BIN
android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png


+ 4 - 0
android/app/src/main/res/values/ic_launcher_background.xml

@@ -0,0 +1,4 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<resources>
3
+    <color name="ic_launcher_background">#FFFFFF</color>
4
+</resources>

+ 7 - 0
android/app/src/main/res/values/strings.xml

@@ -0,0 +1,7 @@
1
+<?xml version='1.0' encoding='utf-8'?>
2
+<resources>
3
+    <string name="app_name">ra100</string>
4
+    <string name="title_activity_main">ra100</string>
5
+    <string name="package_name">io.ionic.starter</string>
6
+    <string name="custom_url_scheme">io.ionic.starter</string>
7
+</resources>

+ 22 - 0
android/app/src/main/res/values/styles.xml

@@ -0,0 +1,22 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<resources>
3
+
4
+    <!-- Base application theme. -->
5
+    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
6
+        <!-- Customize your theme here. -->
7
+        <item name="colorPrimary">@color/colorPrimary</item>
8
+        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
9
+        <item name="colorAccent">@color/colorAccent</item>
10
+    </style>
11
+
12
+    <style name="AppTheme.NoActionBar" parent="Theme.AppCompat.DayNight.NoActionBar">
13
+        <item name="windowActionBar">false</item>
14
+        <item name="windowNoTitle">true</item>
15
+        <item name="android:background">@null</item>
16
+    </style>
17
+
18
+
19
+    <style name="AppTheme.NoActionBarLaunch" parent="Theme.SplashScreen">
20
+        <item name="android:background">@drawable/splash</item>
21
+    </style>
22
+</resources>

+ 5 - 0
android/app/src/main/res/xml/file_paths.xml

@@ -0,0 +1,5 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<paths xmlns:android="http://schemas.android.com/apk/res/android">
3
+    <external-path name="my_images" path="." />
4
+    <cache-path name="my_cache_images" path="." />
5
+</paths>

+ 18 - 0
android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java

@@ -0,0 +1,18 @@
1
+package com.getcapacitor.myapp;
2
+
3
+import static org.junit.Assert.*;
4
+
5
+import org.junit.Test;
6
+
7
+/**
8
+ * Example local unit test, which will execute on the development machine (host).
9
+ *
10
+ * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
11
+ */
12
+public class ExampleUnitTest {
13
+
14
+    @Test
15
+    public void addition_isCorrect() throws Exception {
16
+        assertEquals(4, 2 + 2);
17
+    }
18
+}

+ 29 - 0
android/build.gradle

@@ -0,0 +1,29 @@
1
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
2
+
3
+buildscript {
4
+    
5
+    repositories {
6
+        google()
7
+        mavenCentral()
8
+    }
9
+    dependencies {
10
+        classpath 'com.android.tools.build:gradle:7.2.1'
11
+        classpath 'com.google.gms:google-services:4.3.13'
12
+
13
+        // NOTE: Do not place your application dependencies here; they belong
14
+        // in the individual module build.gradle files
15
+    }
16
+}
17
+
18
+apply from: "variables.gradle"
19
+
20
+allprojects {
21
+    repositories {
22
+        google()
23
+        mavenCentral()
24
+    }
25
+}
26
+
27
+task clean(type: Delete) {
28
+    delete rootProject.buildDir
29
+}

+ 15 - 0
android/capacitor.settings.gradle

@@ -0,0 +1,15 @@
1
+// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN
2
+include ':capacitor-android'
3
+project(':capacitor-android').projectDir = new File('../node_modules/@capacitor/android/capacitor')
4
+
5
+include ':capacitor-app'
6
+project(':capacitor-app').projectDir = new File('../node_modules/@capacitor/app/android')
7
+
8
+include ':capacitor-haptics'
9
+project(':capacitor-haptics').projectDir = new File('../node_modules/@capacitor/haptics/android')
10
+
11
+include ':capacitor-keyboard'
12
+project(':capacitor-keyboard').projectDir = new File('../node_modules/@capacitor/keyboard/android')
13
+
14
+include ':capacitor-status-bar'
15
+project(':capacitor-status-bar').projectDir = new File('../node_modules/@capacitor/status-bar/android')

+ 24 - 0
android/gradle.properties

@@ -0,0 +1,24 @@
1
+# Project-wide Gradle settings.
2
+
3
+# IDE (e.g. Android Studio) users:
4
+# Gradle settings configured through the IDE *will override*
5
+# any settings specified in this file.
6
+
7
+# For more details on how to configure your build environment visit
8
+# http://www.gradle.org/docs/current/userguide/build_environment.html
9
+
10
+# Specifies the JVM arguments used for the daemon process.
11
+# The setting is particularly useful for tweaking memory settings.
12
+org.gradle.jvmargs=-Xmx1536m
13
+
14
+# When configured, Gradle will run in incubating parallel mode.
15
+# This option should only be used with decoupled projects. More details, visit
16
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17
+# org.gradle.parallel=true
18
+
19
+# AndroidX package structure to make it clearer which packages are bundled with the
20
+# Android operating system, and which are packaged with your app's APK
21
+# https://developer.android.com/topic/libraries/support-library/androidx-rn
22
+android.useAndroidX=true
23
+# Automatically convert third-party libraries to use AndroidX
24
+android.enableJetifier=true

BIN
android/gradle/wrapper/gradle-wrapper.jar


+ 5 - 0
android/gradle/wrapper/gradle-wrapper.properties

@@ -0,0 +1,5 @@
1
+distributionBase=GRADLE_USER_HOME
2
+distributionPath=wrapper/dists
3
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-all.zip
4
+zipStoreBase=GRADLE_USER_HOME
5
+zipStorePath=wrapper/dists

+ 234 - 0
android/gradlew

@@ -0,0 +1,234 @@
1
+#!/bin/sh
2
+
3
+#
4
+# Copyright © 2015-2021 the original authors.
5
+#
6
+# Licensed under the Apache License, Version 2.0 (the "License");
7
+# you may not use this file except in compliance with the License.
8
+# You may obtain a copy of the License at
9
+#
10
+#      https://www.apache.org/licenses/LICENSE-2.0
11
+#
12
+# Unless required by applicable law or agreed to in writing, software
13
+# distributed under the License is distributed on an "AS IS" BASIS,
14
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+# See the License for the specific language governing permissions and
16
+# limitations under the License.
17
+#
18
+
19
+##############################################################################
20
+#
21
+#   Gradle start up script for POSIX generated by Gradle.
22
+#
23
+#   Important for running:
24
+#
25
+#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
26
+#       noncompliant, but you have some other compliant shell such as ksh or
27
+#       bash, then to run this script, type that shell name before the whole
28
+#       command line, like:
29
+#
30
+#           ksh Gradle
31
+#
32
+#       Busybox and similar reduced shells will NOT work, because this script
33
+#       requires all of these POSIX shell features:
34
+#         * functions;
35
+#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
36
+#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;
37
+#         * compound commands having a testable exit status, especially «case»;
38
+#         * various built-in commands including «command», «set», and «ulimit».
39
+#
40
+#   Important for patching:
41
+#
42
+#   (2) This script targets any POSIX shell, so it avoids extensions provided
43
+#       by Bash, Ksh, etc; in particular arrays are avoided.
44
+#
45
+#       The "traditional" practice of packing multiple parameters into a
46
+#       space-separated string is a well documented source of bugs and security
47
+#       problems, so this is (mostly) avoided, by progressively accumulating
48
+#       options in "$@", and eventually passing that to Java.
49
+#
50
+#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
51
+#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
52
+#       see the in-line comments for details.
53
+#
54
+#       There are tweaks for specific operating systems such as AIX, CygWin,
55
+#       Darwin, MinGW, and NonStop.
56
+#
57
+#   (3) This script is generated from the Groovy template
58
+#       https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
59
+#       within the Gradle project.
60
+#
61
+#       You can find Gradle at https://github.com/gradle/gradle/.
62
+#
63
+##############################################################################
64
+
65
+# Attempt to set APP_HOME
66
+
67
+# Resolve links: $0 may be a link
68
+app_path=$0
69
+
70
+# Need this for daisy-chained symlinks.
71
+while
72
+    APP_HOME=${app_path%"${app_path##*/}"}  # leaves a trailing /; empty if no leading path
73
+    [ -h "$app_path" ]
74
+do
75
+    ls=$( ls -ld "$app_path" )
76
+    link=${ls#*' -> '}
77
+    case $link in             #(
78
+      /*)   app_path=$link ;; #(
79
+      *)    app_path=$APP_HOME$link ;;
80
+    esac
81
+done
82
+
83
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
84
+
85
+APP_NAME="Gradle"
86
+APP_BASE_NAME=${0##*/}
87
+
88
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
89
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
90
+
91
+# Use the maximum available, or set MAX_FD != -1 to use that value.
92
+MAX_FD=maximum
93
+
94
+warn () {
95
+    echo "$*"
96
+} >&2
97
+
98
+die () {
99
+    echo
100
+    echo "$*"
101
+    echo
102
+    exit 1
103
+} >&2
104
+
105
+# OS specific support (must be 'true' or 'false').
106
+cygwin=false
107
+msys=false
108
+darwin=false
109
+nonstop=false
110
+case "$( uname )" in                #(
111
+  CYGWIN* )         cygwin=true  ;; #(
112
+  Darwin* )         darwin=true  ;; #(
113
+  MSYS* | MINGW* )  msys=true    ;; #(
114
+  NONSTOP* )        nonstop=true ;;
115
+esac
116
+
117
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
118
+
119
+
120
+# Determine the Java command to use to start the JVM.
121
+if [ -n "$JAVA_HOME" ] ; then
122
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
123
+        # IBM's JDK on AIX uses strange locations for the executables
124
+        JAVACMD=$JAVA_HOME/jre/sh/java
125
+    else
126
+        JAVACMD=$JAVA_HOME/bin/java
127
+    fi
128
+    if [ ! -x "$JAVACMD" ] ; then
129
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
130
+
131
+Please set the JAVA_HOME variable in your environment to match the
132
+location of your Java installation."
133
+    fi
134
+else
135
+    JAVACMD=java
136
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
137
+
138
+Please set the JAVA_HOME variable in your environment to match the
139
+location of your Java installation."
140
+fi
141
+
142
+# Increase the maximum file descriptors if we can.
143
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
144
+    case $MAX_FD in #(
145
+      max*)
146
+        MAX_FD=$( ulimit -H -n ) ||
147
+            warn "Could not query maximum file descriptor limit"
148
+    esac
149
+    case $MAX_FD in  #(
150
+      '' | soft) :;; #(
151
+      *)
152
+        ulimit -n "$MAX_FD" ||
153
+            warn "Could not set maximum file descriptor limit to $MAX_FD"
154
+    esac
155
+fi
156
+
157
+# Collect all arguments for the java command, stacking in reverse order:
158
+#   * args from the command line
159
+#   * the main class name
160
+#   * -classpath
161
+#   * -D...appname settings
162
+#   * --module-path (only if needed)
163
+#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
164
+
165
+# For Cygwin or MSYS, switch paths to Windows format before running java
166
+if "$cygwin" || "$msys" ; then
167
+    APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
168
+    CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
169
+
170
+    JAVACMD=$( cygpath --unix "$JAVACMD" )
171
+
172
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
173
+    for arg do
174
+        if
175
+            case $arg in                                #(
176
+              -*)   false ;;                            # don't mess with options #(
177
+              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath
178
+                    [ -e "$t" ] ;;                      #(
179
+              *)    false ;;
180
+            esac
181
+        then
182
+            arg=$( cygpath --path --ignore --mixed "$arg" )
183
+        fi
184
+        # Roll the args list around exactly as many times as the number of
185
+        # args, so each arg winds up back in the position where it started, but
186
+        # possibly modified.
187
+        #
188
+        # NB: a `for` loop captures its iteration list before it begins, so
189
+        # changing the positional parameters here affects neither the number of
190
+        # iterations, nor the values presented in `arg`.
191
+        shift                   # remove old arg
192
+        set -- "$@" "$arg"      # push replacement arg
193
+    done
194
+fi
195
+
196
+# Collect all arguments for the java command;
197
+#   * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
198
+#     shell script including quotes and variable substitutions, so put them in
199
+#     double quotes to make sure that they get re-expanded; and
200
+#   * put everything else in single quotes, so that it's not re-expanded.
201
+
202
+set -- \
203
+        "-Dorg.gradle.appname=$APP_BASE_NAME" \
204
+        -classpath "$CLASSPATH" \
205
+        org.gradle.wrapper.GradleWrapperMain \
206
+        "$@"
207
+
208
+# Use "xargs" to parse quoted args.
209
+#
210
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
211
+#
212
+# In Bash we could simply go:
213
+#
214
+#   readarray ARGS < <( xargs -n1 <<<"$var" ) &&
215
+#   set -- "${ARGS[@]}" "$@"
216
+#
217
+# but POSIX shell has neither arrays nor command substitution, so instead we
218
+# post-process each arg (as a line of input to sed) to backslash-escape any
219
+# character that might be a shell metacharacter, then use eval to reverse
220
+# that process (while maintaining the separation between arguments), and wrap
221
+# the whole thing up as a single "set" statement.
222
+#
223
+# This will of course break if any of these variables contains a newline or
224
+# an unmatched quote.
225
+#
226
+
227
+eval "set -- $(
228
+        printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
229
+        xargs -n1 |
230
+        sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
231
+        tr '\n' ' '
232
+    )" '"$@"'
233
+
234
+exec "$JAVACMD" "$@"

+ 89 - 0
android/gradlew.bat

@@ -0,0 +1,89 @@
1
+@rem
2
+@rem Copyright 2015 the original author or authors.
3
+@rem
4
+@rem Licensed under the Apache License, Version 2.0 (the "License");
5
+@rem you may not use this file except in compliance with the License.
6
+@rem You may obtain a copy of the License at
7
+@rem
8
+@rem      https://www.apache.org/licenses/LICENSE-2.0
9
+@rem
10
+@rem Unless required by applicable law or agreed to in writing, software
11
+@rem distributed under the License is distributed on an "AS IS" BASIS,
12
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+@rem See the License for the specific language governing permissions and
14
+@rem limitations under the License.
15
+@rem
16
+
17
+@if "%DEBUG%" == "" @echo off
18
+@rem ##########################################################################
19
+@rem
20
+@rem  Gradle startup script for Windows
21
+@rem
22
+@rem ##########################################################################
23
+
24
+@rem Set local scope for the variables with windows NT shell
25
+if "%OS%"=="Windows_NT" setlocal
26
+
27
+set DIRNAME=%~dp0
28
+if "%DIRNAME%" == "" set DIRNAME=.
29
+set APP_BASE_NAME=%~n0
30
+set APP_HOME=%DIRNAME%
31
+
32
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
33
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34
+
35
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37
+
38
+@rem Find java.exe
39
+if defined JAVA_HOME goto findJavaFromJavaHome
40
+
41
+set JAVA_EXE=java.exe
42
+%JAVA_EXE% -version >NUL 2>&1
43
+if "%ERRORLEVEL%" == "0" goto execute
44
+
45
+echo.
46
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47
+echo.
48
+echo Please set the JAVA_HOME variable in your environment to match the
49
+echo location of your Java installation.
50
+
51
+goto fail
52
+
53
+:findJavaFromJavaHome
54
+set JAVA_HOME=%JAVA_HOME:"=%
55
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56
+
57
+if exist "%JAVA_EXE%" goto execute
58
+
59
+echo.
60
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61
+echo.
62
+echo Please set the JAVA_HOME variable in your environment to match the
63
+echo location of your Java installation.
64
+
65
+goto fail
66
+
67
+:execute
68
+@rem Setup the command line
69
+
70
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71
+
72
+
73
+@rem Execute Gradle
74
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75
+
76
+:end
77
+@rem End local scope for the variables with windows NT shell
78
+if "%ERRORLEVEL%"=="0" goto mainEnd
79
+
80
+:fail
81
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82
+rem the _cmd.exe /c_ return code!
83
+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84
+exit /b 1
85
+
86
+:mainEnd
87
+if "%OS%"=="Windows_NT" endlocal
88
+
89
+:omega

+ 5 - 0
android/settings.gradle

@@ -0,0 +1,5 @@
1
+include ':app'
2
+include ':capacitor-cordova-android-plugins'
3
+project(':capacitor-cordova-android-plugins').projectDir = new File('./capacitor-cordova-android-plugins/')
4
+
5
+apply from: 'capacitor.settings.gradle'

+ 16 - 0
android/variables.gradle

@@ -0,0 +1,16 @@
1
+ext {
2
+    minSdkVersion = 22
3
+    compileSdkVersion = 32
4
+    targetSdkVersion = 32
5
+    androidxActivityVersion = '1.4.0'
6
+    androidxAppCompatVersion = '1.4.2'
7
+    androidxCoordinatorLayoutVersion = '1.2.0'
8
+    androidxCoreVersion = '1.8.0'
9
+    androidxFragmentVersion = '1.4.1'
10
+    coreSplashScreenVersion = '1.0.0-rc01'
11
+    androidxWebkitVersion = '1.4.0'
12
+    junitVersion = '4.13.2'
13
+    androidxJunitVersion = '1.1.3'
14
+    androidxEspressoCoreVersion = '3.4.0'
15
+    cordovaAndroidVersion = '10.1.1'
16
+}

tum/whitesports - Gogs: Simplico Git Service

暫無描述

class-wp-rest-users-controller.php 45KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575
  1. <?php
  2. /**
  3. * REST API: WP_REST_Users_Controller class
  4. *
  5. * @package WordPress
  6. * @subpackage REST_API
  7. * @since 4.7.0
  8. */
  9. /**
  10. * Core class used to manage users via the REST API.
  11. *
  12. * @since 4.7.0
  13. *
  14. * @see WP_REST_Controller
  15. */
  16. class WP_REST_Users_Controller extends WP_REST_Controller {
  17. /**
  18. * Instance of a user meta fields object.
  19. *
  20. * @since 4.7.0
  21. * @var WP_REST_User_Meta_Fields
  22. */
  23. protected $meta;
  24. /**
  25. * Constructor.
  26. *
  27. * @since 4.7.0
  28. */
  29. public function __construct() {
  30. $this->namespace = 'wp/v2';
  31. $this->rest_base = 'users';
  32. $this->meta = new WP_REST_User_Meta_Fields();
  33. }
  34. /**
  35. * Registers the routes for users.
  36. *
  37. * @since 4.7.0
  38. *
  39. * @see register_rest_route()
  40. */
  41. public function register_routes() {
  42. register_rest_route(
  43. $this->namespace,
  44. '/' . $this->rest_base,
  45. array(
  46. array(
  47. 'methods' => WP_REST_Server::READABLE,
  48. 'callback' => array( $this, 'get_items' ),
  49. 'permission_callback' => array( $this, 'get_items_permissions_check' ),
  50. 'args' => $this->get_collection_params(),
  51. ),
  52. array(
  53. 'methods' => WP_REST_Server::CREATABLE,
  54. 'callback' => array( $this, 'create_item' ),
  55. 'permission_callback' => array( $this, 'create_item_permissions_check' ),
  56. 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
  57. ),
  58. 'schema' => array( $this, 'get_public_item_schema' ),
  59. )
  60. );
  61. register_rest_route(
  62. $this->namespace,
  63. '/' . $this->rest_base . '/(?P<id>[\d]+)',
  64. array(
  65. 'args' => array(
  66. 'id' => array(
  67. 'description' => __( 'Unique identifier for the user.' ),
  68. 'type' => 'integer',
  69. ),
  70. ),
  71. array(
  72. 'methods' => WP_REST_Server::READABLE,
  73. 'callback' => array( $this, 'get_item' ),
  74. 'permission_callback' => array( $this, 'get_item_permissions_check' ),
  75. 'args' => array(
  76. 'context' => $this->get_context_param( array( 'default' => 'view' ) ),
  77. ),
  78. ),
  79. array(
  80. 'methods' => WP_REST_Server::EDITABLE,
  81. 'callback' => array( $this, 'update_item' ),
  82. 'permission_callback' => array( $this, 'update_item_permissions_check' ),
  83. 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
  84. ),
  85. array(
  86. 'methods' => WP_REST_Server::DELETABLE,
  87. 'callback' => array( $this, 'delete_item' ),
  88. 'permission_callback' => array( $this, 'delete_item_permissions_check' ),
  89. 'args' => array(
  90. 'force' => array(
  91. 'type' => 'boolean',
  92. 'default' => false,
  93. 'description' => __( 'Required to be true, as users do not support trashing.' ),
  94. ),
  95. 'reassign' => array(
  96. 'type' => 'integer',
  97. 'description' => __( 'Reassign the deleted user\'s posts and links to this user ID.' ),
  98. 'required' => true,
  99. 'sanitize_callback' => array( $this, 'check_reassign' ),
  100. ),
  101. ),
  102. ),
  103. 'schema' => array( $this, 'get_public_item_schema' ),
  104. )
  105. );
  106. register_rest_route(
  107. $this->namespace,
  108. '/' . $this->rest_base . '/me',
  109. array(
  110. array(
  111. 'methods' => WP_REST_Server::READABLE,
  112. 'permission_callback' => '__return_true',
  113. 'callback' => array( $this, 'get_current_item' ),
  114. 'args' => array(
  115. 'context' => $this->get_context_param( array( 'default' => 'view' ) ),
  116. ),
  117. ),
  118. array(
  119. 'methods' => WP_REST_Server::EDITABLE,
  120. 'callback' => array( $this, 'update_current_item' ),
  121. 'permission_callback' => array( $this, 'update_current_item_permissions_check' ),
  122. 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
  123. ),
  124. array(
  125. 'methods' => WP_REST_Server::DELETABLE,
  126. 'callback' => array( $this, 'delete_current_item' ),
  127. 'permission_callback' => array( $this, 'delete_current_item_permissions_check' ),
  128. 'args' => array(
  129. 'force' => array(
  130. 'type' => 'boolean',
  131. 'default' => false,
  132. 'description' => __( 'Required to be true, as users do not support trashing.' ),
  133. ),
  134. 'reassign' => array(
  135. 'type' => 'integer',
  136. 'description' => __( 'Reassign the deleted user\'s posts and links to this user ID.' ),
  137. 'required' => true,
  138. 'sanitize_callback' => array( $this, 'check_reassign' ),
  139. ),
  140. ),
  141. ),
  142. 'schema' => array( $this, 'get_public_item_schema' ),
  143. )
  144. );
  145. }
  146. /**
  147. * Checks for a valid value for the reassign parameter when deleting users.
  148. *
  149. * The value can be an integer, 'false', false, or ''.
  150. *
  151. * @since 4.7.0
  152. *
  153. * @param int|bool $value The value passed to the reassign parameter.
  154. * @param WP_REST_Request $request Full details about the request.
  155. * @param string $param The parameter that is being sanitized.
  156. * @return int|bool|WP_Error
  157. */
  158. public function check_reassign( $value, $request, $param ) {
  159. if ( is_numeric( $value ) ) {
  160. return $value;
  161. }
  162. if ( empty( $value ) || false === $value || 'false' === $value ) {
  163. return false;
  164. }
  165. return new WP_Error(
  166. 'rest_invalid_param',
  167. __( 'Invalid user parameter(s).' ),
  168. array( 'status' => 400 )
  169. );
  170. }
  171. /**
  172. * Permissions check for getting all users.
  173. *
  174. * @since 4.7.0
  175. *
  176. * @param WP_REST_Request $request Full details about the request.
  177. * @return true|WP_Error True if the request has read access, otherwise WP_Error object.
  178. */
  179. public function get_items_permissions_check( $request ) {
  180. // Check if roles is specified in GET request and if user can list users.
  181. if ( ! empty( $request['roles'] ) && ! current_user_can( 'list_users' ) ) {
  182. return new WP_Error(
  183. 'rest_user_cannot_view',
  184. __( 'Sorry, you are not allowed to filter users by role.' ),
  185. array( 'status' => rest_authorization_required_code() )
  186. );
  187. }
  188. if ( 'edit' === $request['context'] && ! current_user_can( 'list_users' ) ) {
  189. return new WP_Error(
  190. 'rest_forbidden_context',
  191. __( 'Sorry, you are not allowed to list users.' ),
  192. array( 'status' => rest_authorization_required_code() )
  193. );
  194. }
  195. if ( in_array( $request['orderby'], array( 'email', 'registered_date' ), true ) && ! current_user_can( 'list_users' ) ) {
  196. return new WP_Error(
  197. 'rest_forbidden_orderby',
  198. __( 'Sorry, you are not allowed to order users by this parameter.' ),
  199. array( 'status' => rest_authorization_required_code() )
  200. );
  201. }
  202. if ( 'authors' === $request['who'] ) {
  203. $types = get_post_types( array( 'show_in_rest' => true ), 'objects' );
  204. foreach ( $types as $type ) {
  205. if ( post_type_supports( $type->name, 'author' )
  206. && current_user_can( $type->cap->edit_posts ) ) {
  207. return true;
  208. }
  209. }
  210. return new WP_Error(
  211. 'rest_forbidden_who',
  212. __( 'Sorry, you are not allowed to query users by this parameter.' ),
  213. array( 'status' => rest_authorization_required_code() )
  214. );
  215. }
  216. return true;
  217. }
  218. /**
  219. * Retrieves all users.
  220. *
  221. * @since 4.7.0
  222. *
  223. * @param WP_REST_Request $request Full details about the request.
  224. * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
  225. */
  226. public function get_items( $request ) {
  227. // Retrieve the list of registered collection query parameters.
  228. $registered = $this->get_collection_params();
  229. /*
  230. * This array defines mappings between public API query parameters whose
  231. * values are accepted as-passed, and their internal WP_Query parameter
  232. * name equivalents (some are the same). Only values which are also
  233. * present in $registered will be set.
  234. */
  235. $parameter_mappings = array(
  236. 'exclude' => 'exclude',
  237. 'include' => 'include',
  238. 'order' => 'order',
  239. 'per_page' => 'number',
  240. 'search' => 'search',
  241. 'roles' => 'role__in',
  242. 'slug' => 'nicename__in',
  243. );
  244. $prepared_args = array();
  245. /*
  246. * For each known parameter which is both registered and present in the request,
  247. * set the parameter's value on the query $prepared_args.
  248. */
  249. foreach ( $parameter_mappings as $api_param => $wp_param ) {
  250. if ( isset( $registered[ $api_param ], $request[ $api_param ] ) ) {
  251. $prepared_args[ $wp_param ] = $request[ $api_param ];
  252. }
  253. }
  254. if ( isset( $registered['offset'] ) && ! empty( $request['offset'] ) ) {
  255. $prepared_args['offset'] = $request['offset'];
  256. } else {
  257. $prepared_args['offset'] = ( $request['page'] - 1 ) * $prepared_args['number'];
  258. }
  259. if ( isset( $registered['orderby'] ) ) {
  260. $orderby_possibles = array(
  261. 'id' => 'ID',
  262. 'include' => 'include',
  263. 'name' => 'display_name',
  264. 'registered_date' => 'registered',
  265. 'slug' => 'user_nicename',
  266. 'include_slugs' => 'nicename__in',
  267. 'email' => 'user_email',
  268. 'url' => 'user_url',
  269. );
  270. $prepared_args['orderby'] = $orderby_possibles[ $request['orderby'] ];
  271. }
  272. if ( isset( $registered['who'] ) && ! empty( $request['who'] ) && 'authors' === $request['who'] ) {
  273. $prepared_args['who'] = 'authors';
  274. } elseif ( ! current_user_can( 'list_users' ) ) {
  275. $prepared_args['has_published_posts'] = get_post_types( array( 'show_in_rest' => true ), 'names' );
  276. }
  277. if ( ! empty( $prepared_args['search'] ) ) {
  278. $prepared_args['search'] = '*' . $prepared_args['search'] . '*';
  279. }
  280. /**
  281. * Filters WP_User_Query arguments when querying users via the REST API.
  282. *
  283. * @link https://developer.wordpress.org/reference/classes/wp_user_query/
  284. *
  285. * @since 4.7.0
  286. *
  287. * @param array $prepared_args Array of arguments for WP_User_Query.
  288. * @param WP_REST_Request $request The REST API request.
  289. */
  290. $prepared_args = apply_filters( 'rest_user_query', $prepared_args, $request );
  291. $query = new WP_User_Query( $prepared_args );
  292. $users = array();
  293. foreach ( $query->results as $user ) {
  294. $data = $this->prepare_item_for_response( $user, $request );
  295. $users[] = $this->prepare_response_for_collection( $data );
  296. }
  297. $response = rest_ensure_response( $users );
  298. // Store pagination values for headers then unset for count query.
  299. $per_page = (int) $prepared_args['number'];
  300. $page = ceil( ( ( (int) $prepared_args['offset'] ) / $per_page ) + 1 );
  301. $prepared_args['fields'] = 'ID';
  302. $total_users = $query->get_total();
  303. if ( $total_users < 1 ) {
  304. // Out-of-bounds, run the query again without LIMIT for total count.
  305. unset( $prepared_args['number'], $prepared_args['offset'] );
  306. $count_query = new WP_User_Query( $prepared_args );
  307. $total_users = $count_query->get_total();
  308. }
  309. $response->header( 'X-WP-Total', (int) $total_users );
  310. $max_pages = ceil( $total_users / $per_page );
  311. $response->header( 'X-WP-TotalPages', (int) $max_pages );
  312. $base = add_query_arg( urlencode_deep( $request->get_query_params() ), rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ) );
  313. if ( $page > 1 ) {
  314. $prev_page = $page - 1;
  315. if ( $prev_page > $max_pages ) {
  316. $prev_page = $max_pages;
  317. }
  318. $prev_link = add_query_arg( 'page', $prev_page, $base );
  319. $response->link_header( 'prev', $prev_link );
  320. }
  321. if ( $max_pages > $page ) {
  322. $next_page = $page + 1;
  323. $next_link = add_query_arg( 'page', $next_page, $base );
  324. $response->link_header( 'next', $next_link );
  325. }
  326. return $response;
  327. }
  328. /**
  329. * Get the user, if the ID is valid.
  330. *
  331. * @since 4.7.2
  332. *
  333. * @param int $id Supplied ID.
  334. * @return WP_User|WP_Error True if ID is valid, WP_Error otherwise.
  335. */
  336. protected function get_user( $id ) {
  337. $error = new WP_Error(
  338. 'rest_user_invalid_id',
  339. __( 'Invalid user ID.' ),
  340. array( 'status' => 404 )
  341. );
  342. if ( (int) $id <= 0 ) {
  343. return $error;
  344. }
  345. $user = get_userdata( (int) $id );
  346. if ( empty( $user ) || ! $user->exists() ) {
  347. return $error;
  348. }
  349. if ( is_multisite() && ! is_user_member_of_blog( $user->ID ) ) {
  350. return $error;
  351. }
  352. return $user;
  353. }
  354. /**
  355. * Checks if a given request has access to read a user.
  356. *
  357. * @since 4.7.0
  358. *
  359. * @param WP_REST_Request $request Full details about the request.
  360. * @return true|WP_Error True if the request has read access for the item, otherwise WP_Error object.
  361. */
  362. public function get_item_permissions_check( $request ) {
  363. $user = $this->get_user( $request['id'] );
  364. if ( is_wp_error( $user ) ) {
  365. return $user;
  366. }
  367. $types = get_post_types( array( 'show_in_rest' => true ), 'names' );
  368. if ( get_current_user_id() === $user->ID ) {
  369. return true;
  370. }
  371. if ( 'edit' === $request['context'] && ! current_user_can( 'list_users' ) ) {
  372. return new WP_Error(
  373. 'rest_user_cannot_view',
  374. __( 'Sorry, you are not allowed to list users.' ),
  375. array( 'status' => rest_authorization_required_code() )
  376. );
  377. } elseif ( ! count_user_posts( $user->ID, $types ) && ! current_user_can( 'edit_user', $user->ID ) && ! current_user_can( 'list_users' ) ) {
  378. return new WP_Error(
  379. 'rest_user_cannot_view',
  380. __( 'Sorry, you are not allowed to list users.' ),
  381. array( 'status' => rest_authorization_required_code() )
  382. );
  383. }
  384. return true;
  385. }
  386. /**
  387. * Retrieves a single user.
  388. *
  389. * @since 4.7.0
  390. *
  391. * @param WP_REST_Request $request Full details about the request.
  392. * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
  393. */
  394. public function get_item( $request ) {
  395. $user = $this->get_user( $request['id'] );
  396. if ( is_wp_error( $user ) ) {
  397. return $user;
  398. }
  399. $user = $this->prepare_item_for_response( $user, $request );
  400. $response = rest_ensure_response( $user );
  401. return $response;
  402. }
  403. /**
  404. * Retrieves the current user.
  405. *
  406. * @since 4.7.0
  407. *
  408. * @param WP_REST_Request $request Full details about the request.
  409. * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
  410. */
  411. public function get_current_item( $request ) {
  412. $current_user_id = get_current_user_id();
  413. if ( empty( $current_user_id ) ) {
  414. return new WP_Error(
  415. 'rest_not_logged_in',
  416. __( 'You are not currently logged in.' ),
  417. array( 'status' => 401 )
  418. );
  419. }
  420. $user = wp_get_current_user();
  421. $response = $this->prepare_item_for_response( $user, $request );
  422. $response = rest_ensure_response( $response );
  423. return $response;
  424. }
  425. /**
  426. * Checks if a given request has access create users.
  427. *
  428. * @since 4.7.0
  429. *
  430. * @param WP_REST_Request $request Full details about the request.
  431. * @return true|WP_Error True if the request has access to create items, WP_Error object otherwise.
  432. */
  433. public function create_item_permissions_check( $request ) {
  434. if ( ! current_user_can( 'create_users' ) ) {
  435. return new WP_Error(
  436. 'rest_cannot_create_user',
  437. __( 'Sorry, you are not allowed to create new users.' ),
  438. array( 'status' => rest_authorization_required_code() )
  439. );
  440. }
  441. return true;
  442. }
  443. /**
  444. * Creates a single user.
  445. *
  446. * @since 4.7.0
  447. *
  448. * @param WP_REST_Request $request Full details about the request.
  449. * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
  450. */
  451. public function create_item( $request ) {
  452. if ( ! empty( $request['id'] ) ) {
  453. return new WP_Error(
  454. 'rest_user_exists',
  455. __( 'Cannot create existing user.' ),
  456. array( 'status' => 400 )
  457. );
  458. }
  459. $schema = $this->get_item_schema();
  460. if ( ! empty( $request['roles'] ) && ! empty( $schema['properties']['roles'] ) ) {
  461. $check_permission = $this->check_role_update( $request['id'], $request['roles'] );
  462. if ( is_wp_error( $check_permission ) ) {
  463. return $check_permission;
  464. }
  465. }
  466. $user = $this->prepare_item_for_database( $request );
  467. if ( is_multisite() ) {
  468. $ret = wpmu_validate_user_signup( $user->user_login, $user->user_email );
  469. if ( is_wp_error( $ret['errors'] ) && $ret['errors']->has_errors() ) {
  470. $error = new WP_Error(
  471. 'rest_invalid_param',
  472. __( 'Invalid user parameter(s).' ),
  473. array( 'status' => 400 )
  474. );
  475. foreach ( $ret['errors']->errors as $code => $messages ) {
  476. foreach ( $messages as $message ) {
  477. $error->add( $code, $message );
  478. }
  479. $error_data = $error->get_error_data( $code );
  480. if ( $error_data ) {
  481. $error->add_data( $error_data, $code );
  482. }
  483. }
  484. return $error;
  485. }
  486. }
  487. if ( is_multisite() ) {
  488. $user_id = wpmu_create_user( $user->user_login, $user->user_pass, $user->user_email );
  489. if ( ! $user_id ) {
  490. return new WP_Error(
  491. 'rest_user_create',
  492. __( 'Error creating new user.' ),
  493. array( 'status' => 500 )
  494. );
  495. }
  496. $user->ID = $user_id;
  497. $user_id = wp_update_user( wp_slash( (array) $user ) );
  498. if ( is_wp_error( $user_id ) ) {
  499. return $user_id;
  500. }
  501. $result = add_user_to_blog( get_site()->id, $user_id, '' );
  502. if ( is_wp_error( $result ) ) {
  503. return $result;
  504. }
  505. } else {
  506. $user_id = wp_insert_user( wp_slash( (array) $user ) );
  507. if ( is_wp_error( $user_id ) ) {
  508. return $user_id;
  509. }
  510. }
  511. $user = get_user_by( 'id', $user_id );
  512. /**
  513. * Fires immediately after a user is created or updated via the REST API.
  514. *
  515. * @since 4.7.0
  516. *
  517. * @param WP_User $user Inserted or updated user object.
  518. * @param WP_REST_Request $request Request object.
  519. * @param bool $creating True when creating a user, false when updating.
  520. */
  521. do_action( 'rest_insert_user', $user, $request, true );
  522. if ( ! empty( $request['roles'] ) && ! empty( $schema['properties']['roles'] ) ) {
  523. array_map( array( $user, 'add_role' ), $request['roles'] );
  524. }
  525. if ( ! empty( $schema['properties']['meta'] ) && isset( $request['meta'] ) ) {
  526. $meta_update = $this->meta->update_value( $request['meta'], $user_id );
  527. if ( is_wp_error( $meta_update ) ) {
  528. return $meta_update;
  529. }
  530. }
  531. $user = get_user_by( 'id', $user_id );
  532. $fields_update = $this->update_additional_fields_for_object( $user, $request );
  533. if ( is_wp_error( $fields_update ) ) {
  534. return $fields_update;
  535. }
  536. $request->set_param( 'context', 'edit' );
  537. /**
  538. * Fires after a user is completely created or updated via the REST API.
  539. *
  540. * @since 5.0.0
  541. *
  542. * @param WP_User $user Inserted or updated user object.
  543. * @param WP_REST_Request $request Request object.
  544. * @param bool $creating True when creating a user, false when updating.
  545. */
  546. do_action( 'rest_after_insert_user', $user, $request, true );
  547. $response = $this->prepare_item_for_response( $user, $request );
  548. $response = rest_ensure_response( $response );
  549. $response->set_status( 201 );
  550. $response->header( 'Location', rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->rest_base, $user_id ) ) );
  551. return $response;
  552. }
  553. /**
  554. * Checks if a given request has access to update a user.
  555. *
  556. * @since 4.7.0
  557. *
  558. * @param WP_REST_Request $request Full details about the request.
  559. * @return true|WP_Error True if the request has access to update the item, WP_Error object otherwise.
  560. */
  561. public function update_item_permissions_check( $request ) {
  562. $user = $this->get_user( $request['id'] );
  563. if ( is_wp_error( $user ) ) {
  564. return $user;
  565. }
  566. if ( ! empty( $request['roles'] ) ) {
  567. if ( ! current_user_can( 'promote_user', $user->ID ) ) {
  568. return new WP_Error(
  569. 'rest_cannot_edit_roles',
  570. __( 'Sorry, you are not allowed to edit roles of this user.' ),
  571. array( 'status' => rest_authorization_required_code() )
  572. );
  573. }
  574. $request_params = array_keys( $request->get_params() );
  575. sort( $request_params );
  576. // If only 'id' and 'roles' are specified (we are only trying to
  577. // edit roles), then only the 'promote_user' cap is required.
  578. if ( array( 'id', 'roles' ) === $request_params ) {
  579. return true;
  580. }
  581. }
  582. if ( ! current_user_can( 'edit_user', $user->ID ) ) {
  583. return new WP_Error(
  584. 'rest_cannot_edit',
  585. __( 'Sorry, you are not allowed to edit this user.' ),
  586. array( 'status' => rest_authorization_required_code() )
  587. );
  588. }
  589. return true;
  590. }
  591. /**
  592. * Updates a single user.
  593. *
  594. * @since 4.7.0
  595. *
  596. * @param WP_REST_Request $request Full details about the request.
  597. * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
  598. */
  599. public function update_item( $request ) {
  600. $user = $this->get_user( $request['id'] );
  601. if ( is_wp_error( $user ) ) {
  602. return $user;
  603. }
  604. $id = $user->ID;
  605. if ( ! $user ) {
  606. return new WP_Error(
  607. 'rest_user_invalid_id',
  608. __( 'Invalid user ID.' ),
  609. array( 'status' => 404 )
  610. );
  611. }
  612. $owner_id = email_exists( $request['email'] );
  613. if ( $owner_id && $owner_id !== $id ) {
  614. return new WP_Error(
  615. 'rest_user_invalid_email',
  616. __( 'Invalid email address.' ),
  617. array( 'status' => 400 )
  618. );
  619. }
  620. if ( ! empty( $request['username'] ) && $request['username'] !== $user->user_login ) {
  621. return new WP_Error(
  622. 'rest_user_invalid_argument',
  623. __( "Username isn't editable." ),
  624. array( 'status' => 400 )
  625. );
  626. }
  627. if ( ! empty( $request['slug'] ) && $request['slug'] !== $user->user_nicename && get_user_by( 'slug', $request['slug'] ) ) {
  628. return new WP_Error(
  629. 'rest_user_invalid_slug',
  630. __( 'Invalid slug.' ),
  631. array( 'status' => 400 )
  632. );
  633. }
  634. if ( ! empty( $request['roles'] ) ) {
  635. $check_permission = $this->check_role_update( $id, $request['roles'] );
  636. if ( is_wp_error( $check_permission ) ) {
  637. return $check_permission;
  638. }
  639. }
  640. $user = $this->prepare_item_for_database( $request );
  641. // Ensure we're operating on the same user we already checked.
  642. $user->ID = $id;
  643. $user_id = wp_update_user( wp_slash( (array) $user ) );
  644. if ( is_wp_error( $user_id ) ) {
  645. return $user_id;
  646. }
  647. $user = get_user_by( 'id', $user_id );
  648. /** This action is documented in wp-includes/rest-api/endpoints/class-wp-rest-users-controller.php */
  649. do_action( 'rest_insert_user', $user, $request, false );
  650. if ( ! empty( $request['roles'] ) ) {
  651. array_map( array( $user, 'add_role' ), $request['roles'] );
  652. }
  653. $schema = $this->get_item_schema();
  654. if ( ! empty( $schema['properties']['meta'] ) && isset( $request['meta'] ) ) {
  655. $meta_update = $this->meta->update_value( $request['meta'], $id );
  656. if ( is_wp_error( $meta_update ) ) {
  657. return $meta_update;
  658. }
  659. }
  660. $user = get_user_by( 'id', $user_id );
  661. $fields_update = $this->update_additional_fields_for_object( $user, $request );
  662. if ( is_wp_error( $fields_update ) ) {
  663. return $fields_update;
  664. }
  665. $request->set_param( 'context', 'edit' );
  666. /** This action is documented in wp-includes/rest-api/endpoints/class-wp-rest-users-controller.php */
  667. do_action( 'rest_after_insert_user', $user, $request, false );
  668. $response = $this->prepare_item_for_response( $user, $request );
  669. $response = rest_ensure_response( $response );
  670. return $response;
  671. }
  672. /**
  673. * Checks if a given request has access to update the current user.
  674. *
  675. * @since 4.7.0
  676. *
  677. * @param WP_REST_Request $request Full details about the request.
  678. * @return true|WP_Error True if the request has access to update the item, WP_Error object otherwise.
  679. */
  680. public function update_current_item_permissions_check( $request ) {
  681. $request['id'] = get_current_user_id();
  682. return $this->update_item_permissions_check( $request );
  683. }
  684. /**
  685. * Updates the current user.
  686. *
  687. * @since 4.7.0
  688. *
  689. * @param WP_REST_Request $request Full details about the request.
  690. * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
  691. */
  692. public function update_current_item( $request ) {
  693. $request['id'] = get_current_user_id();
  694. return $this->update_item( $request );
  695. }
  696. /**
  697. * Checks if a given request has access delete a user.
  698. *
  699. * @since 4.7.0
  700. *
  701. * @param WP_REST_Request $request Full details about the request.
  702. * @return true|WP_Error True if the request has access to delete the item, WP_Error object otherwise.
  703. */
  704. public function delete_item_permissions_check( $request ) {
  705. $user = $this->get_user( $request['id'] );
  706. if ( is_wp_error( $user ) ) {
  707. return $user;
  708. }
  709. if ( ! current_user_can( 'delete_user', $user->ID ) ) {
  710. return new WP_Error(
  711. 'rest_user_cannot_delete',
  712. __( 'Sorry, you are not allowed to delete this user.' ),
  713. array( 'status' => rest_authorization_required_code() )
  714. );
  715. }
  716. return true;
  717. }
  718. /**
  719. * Deletes a single user.
  720. *
  721. * @since 4.7.0
  722. *
  723. * @param WP_REST_Request $request Full details about the request.
  724. * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
  725. */
  726. public function delete_item( $request ) {
  727. // We don't support delete requests in multisite.
  728. if ( is_multisite() ) {
  729. return new WP_Error(
  730. 'rest_cannot_delete',
  731. __( 'The user cannot be deleted.' ),
  732. array( 'status' => 501 )
  733. );
  734. }
  735. $user = $this->get_user( $request['id'] );
  736. if ( is_wp_error( $user ) ) {
  737. return $user;
  738. }
  739. $id = $user->ID;
  740. $reassign = false === $request['reassign'] ? null : absint( $request['reassign'] );
  741. $force = isset( $request['force'] ) ? (bool) $request['force'] : false;
  742. // We don't support trashing for users.
  743. if ( ! $force ) {
  744. return new WP_Error(
  745. 'rest_trash_not_supported',
  746. /* translators: %s: force=true */
  747. sprintf( __( "Users do not support trashing. Set '%s' to delete." ), 'force=true' ),
  748. array( 'status' => 501 )
  749. );
  750. }
  751. if ( ! empty( $reassign ) ) {
  752. if ( $reassign === $id || ! get_userdata( $reassign ) ) {
  753. return new WP_Error(
  754. 'rest_user_invalid_reassign',
  755. __( 'Invalid user ID for reassignment.' ),
  756. array( 'status' => 400 )
  757. );
  758. }
  759. }
  760. $request->set_param( 'context', 'edit' );
  761. $previous = $this->prepare_item_for_response( $user, $request );
  762. // Include user admin functions to get access to wp_delete_user().
  763. require_once ABSPATH . 'wp-admin/includes/user.php';
  764. $result = wp_delete_user( $id, $reassign );
  765. if ( ! $result ) {
  766. return new WP_Error(
  767. 'rest_cannot_delete',
  768. __( 'The user cannot be deleted.' ),
  769. array( 'status' => 500 )
  770. );
  771. }
  772. $response = new WP_REST_Response();
  773. $response->set_data(
  774. array(
  775. 'deleted' => true,
  776. 'previous' => $previous->get_data(),
  777. )
  778. );
  779. /**
  780. * Fires immediately after a user is deleted via the REST API.
  781. *
  782. * @since 4.7.0
  783. *
  784. * @param WP_User $user The user data.
  785. * @param WP_REST_Response $response The response returned from the API.
  786. * @param WP_REST_Request $request The request sent to the API.
  787. */
  788. do_action( 'rest_delete_user', $user, $response, $request );
  789. return $response;
  790. }
  791. /**
  792. * Checks if a given request has access to delete the current user.
  793. *
  794. * @since 4.7.0
  795. *
  796. * @param WP_REST_Request $request Full details about the request.
  797. * @return true|WP_Error True if the request has access to delete the item, WP_Error object otherwise.
  798. */
  799. public function delete_current_item_permissions_check( $request ) {
  800. $request['id'] = get_current_user_id();
  801. return $this->delete_item_permissions_check( $request );
  802. }
  803. /**
  804. * Deletes the current user.
  805. *
  806. * @since 4.7.0
  807. *
  808. * @param WP_REST_Request $request Full details about the request.
  809. * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
  810. */
  811. public function delete_current_item( $request ) {
  812. $request['id'] = get_current_user_id();
  813. return $this->delete_item( $request );
  814. }
  815. /**
  816. * Prepares a single user output for response.
  817. *
  818. * @since 4.7.0
  819. *
  820. * @param WP_User $user User object.
  821. * @param WP_REST_Request $request Request object.
  822. * @return WP_REST_Response Response object.
  823. */
  824. public function prepare_item_for_response( $user, $request ) {
  825. $data = array();
  826. $fields = $this->get_fields_for_response( $request );
  827. if ( in_array( 'id', $fields, true ) ) {
  828. $data['id'] = $user->ID;
  829. }
  830. if ( in_array( 'username', $fields, true ) ) {
  831. $data['username'] = $user->user_login;
  832. }
  833. if ( in_array( 'name', $fields, true ) ) {
  834. $data['name'] = $user->display_name;
  835. }
  836. if ( in_array( 'first_name', $fields, true ) ) {
  837. $data['first_name'] = $user->first_name;
  838. }
  839. if ( in_array( 'last_name', $fields, true ) ) {
  840. $data['last_name'] = $user->last_name;
  841. }
  842. if ( in_array( 'email', $fields, true ) ) {
  843. $data['email'] = $user->user_email;
  844. }
  845. if ( in_array( 'url', $fields, true ) ) {
  846. $data['url'] = $user->user_url;
  847. }
  848. if ( in_array( 'description', $fields, true ) ) {
  849. $data['description'] = $user->description;
  850. }
  851. if ( in_array( 'link', $fields, true ) ) {
  852. $data['link'] = get_author_posts_url( $user->ID, $user->user_nicename );
  853. }
  854. if ( in_array( 'locale', $fields, true ) ) {
  855. $data['locale'] = get_user_locale( $user );
  856. }
  857. if ( in_array( 'nickname', $fields, true ) ) {
  858. $data['nickname'] = $user->nickname;
  859. }
  860. if ( in_array( 'slug', $fields, true ) ) {
  861. $data['slug'] = $user->user_nicename;
  862. }
  863. if ( in_array( 'roles', $fields, true ) ) {
  864. // Defensively call array_values() to ensure an array is returned.
  865. $data['roles'] = array_values( $user->roles );
  866. }
  867. if ( in_array( 'registered_date', $fields, true ) ) {
  868. $data['registered_date'] = gmdate( 'c', strtotime( $user->user_registered ) );
  869. }
  870. if ( in_array( 'capabilities', $fields, true ) ) {
  871. $data['capabilities'] = (object) $user->allcaps;
  872. }
  873. if ( in_array( 'extra_capabilities', $fields, true ) ) {
  874. $data['extra_capabilities'] = (object) $user->caps;
  875. }
  876. if ( in_array( 'avatar_urls', $fields, true ) ) {
  877. $data['avatar_urls'] = rest_get_avatar_urls( $user );
  878. }
  879. if ( in_array( 'meta', $fields, true ) ) {
  880. $data['meta'] = $this->meta->get_value( $user->ID, $request );
  881. }
  882. $context = ! empty( $request['context'] ) ? $request['context'] : 'embed';
  883. $data = $this->add_additional_fields_to_object( $data, $request );
  884. $data = $this->filter_response_by_context( $data, $context );
  885. // Wrap the data in a response object.
  886. $response = rest_ensure_response( $data );
  887. $response->add_links( $this->prepare_links( $user ) );
  888. /**
  889. * Filters user data returned from the REST API.
  890. *
  891. * @since 4.7.0
  892. *
  893. * @param WP_REST_Response $response The response object.
  894. * @param WP_User $user User object used to create response.
  895. * @param WP_REST_Request $request Request object.
  896. */
  897. return apply_filters( 'rest_prepare_user', $response, $user, $request );
  898. }
  899. /**
  900. * Prepares links for the user request.
  901. *
  902. * @since 4.7.0
  903. *
  904. * @param WP_User $user User object.
  905. * @return array Links for the given user.
  906. */
  907. protected function prepare_links( $user ) {
  908. $links = array(
  909. 'self' => array(
  910. 'href' => rest_url( sprintf( '%s/%s/%d', $this->namespace, $this->rest_base, $user->ID ) ),
  911. ),
  912. 'collection' => array(
  913. 'href' => rest_url( sprintf( '%s/%s', $this->namespace, $this->rest_base ) ),
  914. ),
  915. );
  916. return $links;
  917. }
  918. /**
  919. * Prepares a single user for creation or update.
  920. *
  921. * @since 4.7.0
  922. *
  923. * @param WP_REST_Request $request Request object.
  924. * @return object User object.
  925. */
  926. protected function prepare_item_for_database( $request ) {
  927. $prepared_user = new stdClass;
  928. $schema = $this->get_item_schema();
  929. // Required arguments.
  930. if ( isset( $request['email'] ) && ! empty( $schema['properties']['email'] ) ) {
  931. $prepared_user->user_email = $request['email'];
  932. }
  933. if ( isset( $request['username'] ) && ! empty( $schema['properties']['username'] ) ) {
  934. $prepared_user->user_login = $request['username'];
  935. }
  936. if ( isset( $request['password'] ) && ! empty( $schema['properties']['password'] ) ) {
  937. $prepared_user->user_pass = $request['password'];
  938. }
  939. // Optional arguments.
  940. if ( isset( $request['id'] ) ) {
  941. $prepared_user->ID = absint( $request['id'] );
  942. }
  943. if ( isset( $request['name'] ) && ! empty( $schema['properties']['name'] ) ) {
  944. $prepared_user->display_name = $request['name'];
  945. }
  946. if ( isset( $request['first_name'] ) && ! empty( $schema['properties']['first_name'] ) ) {
  947. $prepared_user->first_name = $request['first_name'];
  948. }
  949. if ( isset( $request['last_name'] ) && ! empty( $schema['properties']['last_name'] ) ) {
  950. $prepared_user->last_name = $request['last_name'];
  951. }
  952. if ( isset( $request['nickname'] ) && ! empty( $schema['properties']['nickname'] ) ) {
  953. $prepared_user->nickname = $request['nickname'];
  954. }
  955. if ( isset( $request['slug'] ) && ! empty( $schema['properties']['slug'] ) ) {
  956. $prepared_user->user_nicename = $request['slug'];
  957. }
  958. if ( isset( $request['description'] ) && ! empty( $schema['properties']['description'] ) ) {
  959. $prepared_user->description = $request['description'];
  960. }
  961. if ( isset( $request['url'] ) && ! empty( $schema['properties']['url'] ) ) {
  962. $prepared_user->user_url = $request['url'];
  963. }
  964. if ( isset( $request['locale'] ) && ! empty( $schema['properties']['locale'] ) ) {
  965. $prepared_user->locale = $request['locale'];
  966. }
  967. // Setting roles will be handled outside of this function.
  968. if ( isset( $request['roles'] ) ) {
  969. $prepared_user->role = false;
  970. }
  971. /**
  972. * Filters user data before insertion via the REST API.
  973. *
  974. * @since 4.7.0
  975. *
  976. * @param object $prepared_user User object.
  977. * @param WP_REST_Request $request Request object.
  978. */
  979. return apply_filters( 'rest_pre_insert_user', $prepared_user, $request );
  980. }
  981. /**
  982. * Determines if the current user is allowed to make the desired roles change.
  983. *
  984. * @since 4.7.0
  985. *
  986. * @param int $user_id User ID.
  987. * @param array $roles New user roles.
  988. * @return true|WP_Error True if the current user is allowed to make the role change,
  989. * otherwise a WP_Error object.
  990. */
  991. protected function check_role_update( $user_id, $roles ) {
  992. global $wp_roles;
  993. foreach ( $roles as $role ) {
  994. if ( ! isset( $wp_roles->role_objects[ $role ] ) ) {
  995. return new WP_Error(
  996. 'rest_user_invalid_role',
  997. /* translators: %s: Role key. */
  998. sprintf( __( 'The role %s does not exist.' ), $role ),
  999. array( 'status' => 400 )
  1000. );
  1001. }
  1002. $potential_role = $wp_roles->role_objects[ $role ];
  1003. /*
  1004. * Don't let anyone with 'edit_users' (admins) edit their own role to something without it.
  1005. * Multisite super admins can freely edit their blog roles -- they possess all caps.
  1006. */
  1007. if ( ! ( is_multisite()
  1008. && current_user_can( 'manage_sites' ) )
  1009. && get_current_user_id() === $user_id
  1010. && ! $potential_role->has_cap( 'edit_users' )
  1011. ) {
  1012. return new WP_Error(
  1013. 'rest_user_invalid_role',
  1014. __( 'Sorry, you are not allowed to give users that role.' ),
  1015. array( 'status' => rest_authorization_required_code() )
  1016. );
  1017. }
  1018. // Include user admin functions to get access to get_editable_roles().
  1019. require_once ABSPATH . 'wp-admin/includes/user.php';
  1020. // The new role must be editable by the logged-in user.
  1021. $editable_roles = get_editable_roles();
  1022. if ( empty( $editable_roles[ $role ] ) ) {
  1023. return new WP_Error(
  1024. 'rest_user_invalid_role',
  1025. __( 'Sorry, you are not allowed to give users that role.' ),
  1026. array( 'status' => 403 )
  1027. );
  1028. }
  1029. }
  1030. return true;
  1031. }
  1032. /**
  1033. * Check a username for the REST API.
  1034. *
  1035. * Performs a couple of checks like edit_user() in wp-admin/includes/user.php.
  1036. *
  1037. * @since 4.7.0
  1038. *
  1039. * @param string $value The username submitted in the request.
  1040. * @param WP_REST_Request $request Full details about the request.
  1041. * @param string $param The parameter name.
  1042. * @return string|WP_Error The sanitized username, if valid, otherwise an error.
  1043. */
  1044. public function check_username( $value, $request, $param ) {
  1045. $username = (string) $value;
  1046. if ( ! validate_username( $username ) ) {
  1047. return new WP_Error(
  1048. 'rest_user_invalid_username',
  1049. __( 'This username is invalid because it uses illegal characters. Please enter a valid username.' ),
  1050. array( 'status' => 400 )
  1051. );
  1052. }
  1053. /** This filter is documented in wp-includes/user.php */
  1054. $illegal_logins = (array) apply_filters( 'illegal_user_logins', array() );
  1055. if ( in_array( strtolower( $username ), array_map( 'strtolower', $illegal_logins ), true ) ) {
  1056. return new WP_Error(
  1057. 'rest_user_invalid_username',
  1058. __( 'Sorry, that username is not allowed.' ),
  1059. array( 'status' => 400 )
  1060. );
  1061. }
  1062. return $username;
  1063. }
  1064. /**
  1065. * Check a user password for the REST API.
  1066. *
  1067. * Performs a couple of checks like edit_user() in wp-admin/includes/user.php.
  1068. *
  1069. * @since 4.7.0
  1070. *
  1071. * @param string $value The password submitted in the request.
  1072. * @param WP_REST_Request $request Full details about the request.
  1073. * @param string $param The parameter name.
  1074. * @return string|WP_Error The sanitized password, if valid, otherwise an error.
  1075. */
  1076. public function check_user_password( $value, $request, $param ) {
  1077. $password = (string) $value;
  1078. if ( empty( $password ) ) {
  1079. return new WP_Error(
  1080. 'rest_user_invalid_password',
  1081. __( 'Passwords cannot be empty.' ),
  1082. array( 'status' => 400 )
  1083. );
  1084. }
  1085. if ( false !== strpos( $password, '\\' ) ) {
  1086. return new WP_Error(
  1087. 'rest_user_invalid_password',
  1088. sprintf(
  1089. /* translators: %s: The '\' character. */
  1090. __( 'Passwords cannot contain the "%s" character.' ),
  1091. '\\'
  1092. ),
  1093. array( 'status' => 400 )
  1094. );
  1095. }
  1096. return $password;
  1097. }
  1098. /**
  1099. * Retrieves the user's schema, conforming to JSON Schema.
  1100. *
  1101. * @since 4.7.0
  1102. *
  1103. * @return array Item schema data.
  1104. */
  1105. public function get_item_schema() {
  1106. if ( $this->schema ) {
  1107. return $this->add_additional_fields_schema( $this->schema );
  1108. }
  1109. $schema = array(
  1110. '$schema' => 'http://json-schema.org/draft-04/schema#',
  1111. 'title' => 'user',
  1112. 'type' => 'object',
  1113. 'properties' => array(
  1114. 'id' => array(
  1115. 'description' => __( 'Unique identifier for the user.' ),
  1116. 'type' => 'integer',
  1117. 'context' => array( 'embed', 'view', 'edit' ),
  1118. 'readonly' => true,
  1119. ),
  1120. 'username' => array(
  1121. 'description' => __( 'Login name for the user.' ),
  1122. 'type' => 'string',
  1123. 'context' => array( 'edit' ),
  1124. 'required' => true,
  1125. 'arg_options' => array(
  1126. 'sanitize_callback' => array( $this, 'check_username' ),
  1127. ),
  1128. ),
  1129. 'name' => array(
  1130. 'description' => __( 'Display name for the user.' ),
  1131. 'type' => 'string',
  1132. 'context' => array( 'embed', 'view', 'edit' ),
  1133. 'arg_options' => array(
  1134. 'sanitize_callback' => 'sanitize_text_field',
  1135. ),
  1136. ),
  1137. 'first_name' => array(
  1138. 'description' => __( 'First name for the user.' ),
  1139. 'type' => 'string',
  1140. 'context' => array( 'edit' ),
  1141. 'arg_options' => array(
  1142. 'sanitize_callback' => 'sanitize_text_field',
  1143. ),
  1144. ),
  1145. 'last_name' => array(
  1146. 'description' => __( 'Last name for the user.' ),
  1147. 'type' => 'string',
  1148. 'context' => array( 'edit' ),
  1149. 'arg_options' => array(
  1150. 'sanitize_callback' => 'sanitize_text_field',
  1151. ),
  1152. ),
  1153. 'email' => array(
  1154. 'description' => __( 'The email address for the user.' ),
  1155. 'type' => 'string',
  1156. 'format' => 'email',
  1157. 'context' => array( 'edit' ),
  1158. 'required' => true,
  1159. ),
  1160. 'url' => array(
  1161. 'description' => __( 'URL of the user.' ),
  1162. 'type' => 'string',
  1163. 'format' => 'uri',
  1164. 'context' => array( 'embed', 'view', 'edit' ),
  1165. ),
  1166. 'description' => array(
  1167. 'description' => __( 'Description of the user.' ),
  1168. 'type' => 'string',
  1169. 'context' => array( 'embed', 'view', 'edit' ),
  1170. ),
  1171. 'link' => array(
  1172. 'description' => __( 'Author URL of the user.' ),
  1173. 'type' => 'string',
  1174. 'format' => 'uri',
  1175. 'context' => array( 'embed', 'view', 'edit' ),
  1176. 'readonly' => true,
  1177. ),
  1178. 'locale' => array(
  1179. 'description' => __( 'Locale for the user.' ),
  1180. 'type' => 'string',
  1181. 'enum' => array_merge( array( '', 'en_US' ), get_available_languages() ),
  1182. 'context' => array( 'edit' ),
  1183. ),
  1184. 'nickname' => array(
  1185. 'description' => __( 'The nickname for the user.' ),
  1186. 'type' => 'string',
  1187. 'context' => array( 'edit' ),
  1188. 'arg_options' => array(
  1189. 'sanitize_callback' => 'sanitize_text_field',
  1190. ),
  1191. ),
  1192. 'slug' => array(
  1193. 'description' => __( 'An alphanumeric identifier for the user.' ),
  1194. 'type' => 'string',
  1195. 'context' => array( 'embed', 'view', 'edit' ),
  1196. 'arg_options' => array(
  1197. 'sanitize_callback' => array( $this, 'sanitize_slug' ),
  1198. ),
  1199. ),
  1200. 'registered_date' => array(
  1201. 'description' => __( 'Registration date for the user.' ),
  1202. 'type' => 'string',
  1203. 'format' => 'date-time',
  1204. 'context' => array( 'edit' ),
  1205. 'readonly' => true,
  1206. ),
  1207. 'roles' => array(
  1208. 'description' => __( 'Roles assigned to the user.' ),
  1209. 'type' => 'array',
  1210. 'items' => array(
  1211. 'type' => 'string',
  1212. ),
  1213. 'context' => array( 'edit' ),
  1214. ),
  1215. 'password' => array(
  1216. 'description' => __( 'Password for the user (never included).' ),
  1217. 'type' => 'string',
  1218. 'context' => array(), // Password is never displayed.
  1219. 'required' => true,
  1220. 'arg_options' => array(
  1221. 'sanitize_callback' => array( $this, 'check_user_password' ),
  1222. ),
  1223. ),
  1224. 'capabilities' => array(
  1225. 'description' => __( 'All capabilities assigned to the user.' ),
  1226. 'type' => 'object',
  1227. 'context' => array( 'edit' ),
  1228. 'readonly' => true,
  1229. ),
  1230. 'extra_capabilities' => array(
  1231. 'description' => __( 'Any extra capabilities assigned to the user.' ),
  1232. 'type' => 'object',
  1233. 'context' => array( 'edit' ),
  1234. 'readonly' => true,
  1235. ),
  1236. ),
  1237. );
  1238. if ( get_option( 'show_avatars' ) ) {
  1239. $avatar_properties = array();
  1240. $avatar_sizes = rest_get_avatar_sizes();
  1241. foreach ( $avatar_sizes as $size ) {
  1242. $avatar_properties[ $size ] = array(
  1243. /* translators: %d: Avatar image size in pixels. */
  1244. 'description' => sprintf( __( 'Avatar URL with image size of %d pixels.' ), $size ),
  1245. 'type' => 'string',
  1246. 'format' => 'uri',
  1247. 'context' => array( 'embed', 'view', 'edit' ),
  1248. );
  1249. }
  1250. $schema['properties']['avatar_urls'] = array(
  1251. 'description' => __( 'Avatar URLs for the user.' ),
  1252. 'type' => 'object',
  1253. 'context' => array( 'embed', 'view', 'edit' ),
  1254. 'readonly' => true,
  1255. 'properties' => $avatar_properties,
  1256. );
  1257. }
  1258. $schema['properties']['meta'] = $this->meta->get_field_schema();
  1259. $this->schema = $schema;
  1260. return $this->add_additional_fields_schema( $this->schema );
  1261. }
  1262. /**
  1263. * Retrieves the query params for collections.
  1264. *
  1265. * @since 4.7.0
  1266. *
  1267. * @return array Collection parameters.
  1268. */
  1269. public function get_collection_params() {
  1270. $query_params = parent::get_collection_params();
  1271. $query_params['context']['default'] = 'view';
  1272. $query_params['exclude'] = array(
  1273. 'description' => __( 'Ensure result set excludes specific IDs.' ),
  1274. 'type' => 'array',
  1275. 'items' => array(
  1276. 'type' => 'integer',
  1277. ),
  1278. 'default' => array(),
  1279. );
  1280. $query_params['include'] = array(
  1281. 'description' => __( 'Limit result set to specific IDs.' ),
  1282. 'type' => 'array',
  1283. 'items' => array(
  1284. 'type' => 'integer',
  1285. ),
  1286. 'default' => array(),
  1287. );
  1288. $query_params['offset'] = array(
  1289. 'description' => __( 'Offset the result set by a specific number of items.' ),
  1290. 'type' => 'integer',
  1291. );
  1292. $query_params['order'] = array(
  1293. 'default' => 'asc',
  1294. 'description' => __( 'Order sort attribute ascending or descending.' ),
  1295. 'enum' => array( 'asc', 'desc' ),
  1296. 'type' => 'string',
  1297. );
  1298. $query_params['orderby'] = array(
  1299. 'default' => 'name',
  1300. 'description' => __( 'Sort collection by user attribute.' ),
  1301. 'enum' => array(
  1302. 'id',
  1303. 'include',
  1304. 'name',
  1305. 'registered_date',
  1306. 'slug',
  1307. 'include_slugs',
  1308. 'email',
  1309. 'url',
  1310. ),
  1311. 'type' => 'string',
  1312. );
  1313. $query_params['slug'] = array(
  1314. 'description' => __( 'Limit result set to users with one or more specific slugs.' ),
  1315. 'type' => 'array',
  1316. 'items' => array(
  1317. 'type' => 'string',
  1318. ),
  1319. );
  1320. $query_params['roles'] = array(
  1321. 'description' => __( 'Limit result set to users matching at least one specific role provided. Accepts csv list or single role.' ),
  1322. 'type' => 'array',
  1323. 'items' => array(
  1324. 'type' => 'string',
  1325. ),
  1326. );
  1327. $query_params['who'] = array(
  1328. 'description' => __( 'Limit result set to users who are considered authors.' ),
  1329. 'type' => 'string',
  1330. 'enum' => array(
  1331. 'authors',
  1332. ),
  1333. );
  1334. /**
  1335. * Filters REST API collection parameters for the users controller.
  1336. *
  1337. * This filter registers the collection parameter, but does not map the
  1338. * collection parameter to an internal WP_User_Query parameter. Use the
  1339. * `rest_user_query` filter to set WP_User_Query arguments.
  1340. *
  1341. * @since 4.7.0
  1342. *
  1343. * @param array $query_params JSON Schema-formatted collection parameters.
  1344. */
  1345. return apply_filters( 'rest_user_collection_params', $query_params );
  1346. }
  1347. }