tivity_main">whitesports</string> 5
-    <string name="package_name">net.simplico.whitesports</string>
6
-    <string name="custom_url_scheme">net.simplico.whitesports</string>
7
-</resources>

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

@@ -1,22 +0,0 @@
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.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="AppTheme.NoActionBar">
20
-        <item name="android:background">@drawable/splash</item>
21
-    </style>
22
-</resources>

+ 0 - 46
android/app/src/main/res/xml/config.xml

@@ -1,46 +0,0 @@
1
-<?xml version='1.0' encoding='utf-8'?>
2
-<widget version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
3
-  <access origin="*" />
4
-  
5
-  <feature name="CordovaHttpPlugin">
6
-    <param name="android-package" value="com.synconset.cordovahttp.CordovaHttpPlugin"/>
7
-  </feature>
8
-
9
-  <feature name="File">
10
-    <param name="android-package" value="org.apache.cordova.file.FileUtils"/>
11
-    <param name="onload" value="true"/>
12
-  </feature>
13
-
14
-  <feature name="NativeStorage">
15
-    <param name="android-package" value="NativeStorage"/>
16
-  </feature>
17
-
18
-  <feature name="Device">
19
-    <param name="android-package" value="org.apache.cordova.device.Device"/>
20
-  </feature>
21
-
22
-  <feature name="InAppBrowser">
23
-    <param name="android-package" value="org.apache.cordova.inappbrowser.InAppBrowser"/>
24
-  </feature>
25
-
26
-  <feature name="UserAgent">
27
-    <param name="android-package" value="im.ltdev.cordova.UserAgent"/>
28
-    <param name="onload" value="true"/>
29
-  </feature>
30
-
31
-  <feature name="Whitelist">
32
-    <param name="android-package" value="org.apache.cordova.whitelist.WhitelistPlugin"/>
33
-    <param name="onload" value="true"/>
34
-  </feature>
35
-
36
-  
37
-  <preference name="ScrollEnabled" value="false" />
38
-  <preference name="android-minSdkVersion" value="28" />
39
-  <preference name="android-targetSdkVersion" value="28" />
40
-  <preference name="BackupWebStorage" value="none" />
41
-  <preference name="SplashMaintainAspectRatio" value="true" />
42
-  <preference name="FadeSplashScreenDuration" value="300" />
43
-  <preference name="SplashShowOnlyFirstTime" value="false" />
44
-  <preference name="SplashScreen" value="screen" />
45
-  <preference name="SplashScreenDelay" value="3000" />
46
-</widget>

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

@@ -1,5 +0,0 @@
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>

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

@@ -1,18 +0,0 @@
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
-}

+ 0 - 29
android/build.gradle

@@ -1,29 +0,0 @@
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
-        jcenter()
8
-    }
9
-    dependencies {
10
-        classpath 'com.android.tools.build:gradle:4.2.1'
11
-        classpath 'com.google.gms:google-services:4.3.5'
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
-        jcenter()
24
-    }
25
-}
26
-
27
-task clean(type: Delete) {
28
-    delete rootProject.buildDir
29
-}

+ 0 - 15
android/capacitor.settings.gradle

@@ -1,15 +0,0 @@
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')

+ 0 - 24
android/gradle.properties

@@ -1,24 +0,0 @@
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


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

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

+ 0 - 185
android/gradlew

@@ -1,185 +0,0 @@
1
-#!/usr/bin/env sh
2
-
3
-#
4
-# Copyright 2015 the original author or 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 UN*X
22
-##
23
-##############################################################################
24
-
25
-# Attempt to set APP_HOME
26
-# Resolve links: $0 may be a link
27
-PRG="$0"
28
-# Need this for relative symlinks.
29
-while [ -h "$PRG" ] ; do
30
-    ls=`ls -ld "$PRG"`
31
-    link=`expr "$ls" : '.*-> \(.*\)$'`
32
-    if expr "$link" : '/.*' > /dev/null; then
33
-        PRG="$link"
34
-    else
35
-        PRG=`dirname "$PRG"`"/$link"
36
-    fi
37
-done
38
-SAVED="`pwd`"
39
-cd "`dirname \"$PRG\"`/" >/dev/null
40
-APP_HOME="`pwd -P`"
41
-cd "$SAVED" >/dev/null
42
-
43
-APP_NAME="Gradle"
44
-APP_BASE_NAME=`basename "$0"`
45
-
46
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
47
-DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
48
-
49
-# Use the maximum available, or set MAX_FD != -1 to use that value.
50
-MAX_FD="maximum"
51
-
52
-warn () {
53
-    echo "$*"
54
-}
55
-
56
-die () {
57
-    echo
58
-    echo "$*"
59
-    echo
60
-    exit 1
61
-}
62
-
63
-# OS specific support (must be 'true' or 'false').
64
-cygwin=false
65
-msys=false
66
-darwin=false
67
-nonstop=false
68
-case "`uname`" in
69
-  CYGWIN* )
70
-    cygwin=true
71
-    ;;
72
-  Darwin* )
73
-    darwin=true
74
-    ;;
75
-  MINGW* )
76
-    msys=true
77
-    ;;
78
-  NONSTOP* )
79
-    nonstop=true
80
-    ;;
81
-esac
82
-
83
-CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
84
-
85
-
86
-# Determine the Java command to use to start the JVM.
87
-if [ -n "$JAVA_HOME" ] ; then
88
-    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
89
-        # IBM's JDK on AIX uses strange locations for the executables
90
-        JAVACMD="$JAVA_HOME/jre/sh/java"
91
-    else
92
-        JAVACMD="$JAVA_HOME/bin/java"
93
-    fi
94
-    if [ ! -x "$JAVACMD" ] ; then
95
-        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
96
-
97
-Please set the JAVA_HOME variable in your environment to match the
98
-location of your Java installation."
99
-    fi
100
-else
101
-    JAVACMD="java"
102
-    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
103
-
104
-Please set the JAVA_HOME variable in your environment to match the
105
-location of your Java installation."
106
-fi
107
-
108
-# Increase the maximum file descriptors if we can.
109
-if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
110
-    MAX_FD_LIMIT=`ulimit -H -n`
111
-    if [ $? -eq 0 ] ; then
112
-        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
113
-            MAX_FD="$MAX_FD_LIMIT"
114
-        fi
115
-        ulimit -n $MAX_FD
116
-        if [ $? -ne 0 ] ; then
117
-            warn "Could not set maximum file descriptor limit: $MAX_FD"
118
-        fi
119
-    else
120
-        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
121
-    fi
122
-fi
123
-
124
-# For Darwin, add options to specify how the application appears in the dock
125
-if $darwin; then
126
-    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
127
-fi
128
-
129
-# For Cygwin or MSYS, switch paths to Windows format before running java
130
-if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
131
-    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
132
-    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
133
-
134
-    JAVACMD=`cygpath --unix "$JAVACMD"`
135
-
136
-    # We build the pattern for arguments to be converted via cygpath
137
-    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
138
-    SEP=""
139
-    for dir in $ROOTDIRSRAW ; do
140
-        ROOTDIRS="$ROOTDIRS$SEP$dir"
141
-        SEP="|"
142
-    done
143
-    OURCYGPATTERN="(^($ROOTDIRS))"
144
-    # Add a user-defined pattern to the cygpath arguments
145
-    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
146
-        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
147
-    fi
148
-    # Now convert the arguments - kludge to limit ourselves to /bin/sh
149
-    i=0
150
-    for arg in "$@" ; do
151
-        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
152
-        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
153
-
154
-        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
155
-            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
156
-        else
157
-            eval `echo args$i`="\"$arg\""
158
-        fi
159
-        i=`expr $i + 1`
160
-    done
161
-    case $i in
162
-        0) set -- ;;
163
-        1) set -- "$args0" ;;
164
-        2) set -- "$args0" "$args1" ;;
165
-        3) set -- "$args0" "$args1" "$args2" ;;
166
-        4) set -- "$args0" "$args1" "$args2" "$args3" ;;
167
-        5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
168
-        6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
169
-        7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
170
-        8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
171
-        9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
172
-    esac
173
-fi
174
-
175
-# Escape application args
176
-save () {
177
-    for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
178
-    echo " "
179
-}
180
-APP_ARGS=`save "$@"`
181
-
182
-# Collect all arguments for the java command, following the shell quoting and substitution rules
183
-eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
184
-
185
-exec "$JAVACMD" "$@"

+ 0 - 89
android/gradlew.bat

@@ -1,89 +0,0 @@
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

+ 0 - 5
android/settings.gradle

@@ -1,5 +0,0 @@
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'

+ 0 - 14
android/variables.gradle

@@ -1,14 +0,0 @@
1
-ext {
2
-    minSdkVersion = 21
3
-    compileSdkVersion = 30
4
-    targetSdkVersion = 30
5
-    androidxActivityVersion = '1.2.0'
6
-    androidxAppCompatVersion = '1.2.0'
7
-    androidxCoordinatorLayoutVersion = '1.1.0'
8
-    androidxCoreVersion = '1.3.2'
9
-    androidxFragmentVersion = '1.3.0'
10
-    junitVersion = '4.13.1'
11
-    androidxJunitVersion = '1.1.2'
12
-    androidxEspressoCoreVersion = '3.3.0'
13
-    cordovaAndroidVersion = '7.0.0'
14
-}

+ 0 - 23
capacitor.config.ts

@@ -1,23 +0,0 @@
1
-import { CapacitorConfig } from '@capacitor/cli';
2
-
3
-const config: CapacitorConfig = {
4
-  appId: 'io.ionic.starter',
5
-  appName: 'Stark',
6
-  webDir: 'www',
7
-  bundledWebRuntime: false,
8
-  cordova: {
9
-    preferences: {
10
-      ScrollEnabled: 'false',
11
-      'android-minSdkVersion': '28',
12
-      'android-targetSdkVersion': '28',
13
-      BackupWebStorage: 'none',
14
-      SplashMaintainAspectRatio: 'true',
15
-      FadeSplashScreenDuration: '300',
16
-      SplashShowOnlyFirstTime: 'false',
17
-      SplashScreen: 'screen',
18
-      SplashScreenDelay: '3000'
19
-    }
20
-  }
21
-};
22
-
23
-export default config;

+ 5 - 7
config.xml

@@ -1,7 +1,7 @@
1 1
 <?xml version='1.0' encoding='utf-8'?>
2
-<widget id="net.simplico.whitesports" version="0.1.3" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
2
+<widget id="net.simplico.whitesports" version="1.2.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
3 3
     <name>White Sports</name>
4
-    <description>Burn More, Get More</description>
4
+    <description>Virtual Run Mobile App</description>
5 5
     <author email="patumos@gmail.com" href="http://www.simplico.net/">Suebpong Sittimalairut</author>
6 6
     <content src="index.html" />
7 7
     <access origin="*" />
@@ -13,14 +13,15 @@
13 13
     <allow-intent href="geo:*" />
14 14
     <allow-intent href="line://*/*" />
15 15
     <preference name="ScrollEnabled" value="false" />
16
-    <preference name="android-minSdkVersion" value="28" />
17
-    <preference name="android-targetSdkVersion" value="28" />
16
+    <preference name="android-minSdkVersion" value="30" />
17
+    <preference name="android-targetSdkVersion" value="30" />
18 18
     <preference name="BackupWebStorage" value="none" />
19 19
     <preference name="SplashMaintainAspectRatio" value="true" />
20 20
     <preference name="FadeSplashScreenDuration" value="300" />
21 21
     <preference name="SplashShowOnlyFirstTime" value="false" />
22 22
     <preference name="SplashScreen" value="screen" />
23 23
     <preference name="SplashScreenDelay" value="3000" />
24
+    <preference name="InAppBrowserOverrideUserAgent" value="Mozilla/5.0 Google" />
24 25
     <platform name="android">
25 26
         <allow-intent href="market:*" />
26 27
         <icon density="ldpi" src="resources/android/icon/drawable-ldpi-icon.png" />
@@ -60,9 +61,6 @@
60 61
         <icon height="76" src="resources/ios/icon/icon-76.png" width="76" />
61 62
         <icon height="152" src="resources/ios/icon/icon-76@2x.png" width="152" />
62 63
         <icon height="167" src="resources/ios/icon/icon-83.5@2x.png" width="167" />
63
-        <icon height="29" src="resources/ios/icon/icon-small.png" width="29" />
64
-        <icon height="58" src="resources/ios/icon/icon-small@2x.png" width="58" />
65
-        <icon height="87" src="resources/ios/icon/icon-small@3x.png" width="87" />
66 64
         <icon height="1024" src="resources/ios/icon/icon-1024.png" width="1024" />
67 65
         <splash height="1136" src="resources/ios/splash/Default-568h@2x~iphone.png" width="640" />
68 66
         <splash height="1334" src="resources/ios/splash/Default-667h.png" width="750" />

+ 1 - 2
ionic.config.json

@@ -1,8 +1,7 @@
1 1
 {
2 2
   "name": "Stark",
3 3
   "integrations": {
4
-    "cordova": {},
5
-    "capacitor": {}
4
+    "cordova": {}
6 5
   },
7 6
   "type": "ionic-angular"
8 7
 }

BIN
my-release-key.keystore


File diff suppressed because it is too large
+ 108 - 1060
package-lock.json


+ 8 - 26
package.json

@@ -21,19 +21,12 @@
21 21
     "@angular/http": "5.2.10",
22 22
     "@angular/platform-browser": "5.2.10",
23 23
     "@angular/platform-browser-dynamic": "5.2.10",
24
-    "@capacitor/android": "^3.2.5",
25
-    "@capacitor/app": "1.0.3",
26
-    "@capacitor/core": "3.2.5",
27
-    "@capacitor/haptics": "1.1.0",
28
-    "@capacitor/ios": "^3.2.4",
29
-    "@capacitor/keyboard": "1.1.0",
30
-    "@capacitor/status-bar": "1.0.3",
31 24
     "@ionic-native/core": "4.7.0",
32 25
     "@ionic-native/device": "^4.7.0",
33 26
     "@ionic-native/health": "^4.7.0",
34
-    "@ionic-native/http": "^4.9.1",
27
+    "@ionic-native/http": "^4.20.0",
35 28
     "@ionic-native/in-app-browser": "^4.7.0",
36
-    "@ionic-native/native-storage": "^4.20.0",
29
+    "@ionic-native/native-storage": "^4.7.0",
37 30
     "@ionic-native/social-sharing": "^4.7.0",
38 31
     "@ionic-native/splash-screen": "4.7.0",
39 32
     "@ionic-native/status-bar": "4.7.0",
@@ -42,8 +35,8 @@
42 35
     "@swimlane/ngx-charts": "^8.1.0",
43 36
     "angular2-humanize": "^2.1.0",
44 37
     "cordova-plugin-advanced-http": "^1.11.1",
45
-    "cordova-plugin-file": "^6.0.2",
46
-    "cordova-plugin-nativestorage": "^2.3.2",
38
+    "cordova-plugin-file": "^6.0.1",
39
+    "cordova-plugin-ionic-webview": "^5.0.0",
47 40
     "es6-promise-plugin": "4.2.2",
48 41
     "ionic-angular": "3.9.2",
49 42
     "ionicons": "3.0.0",
@@ -56,38 +49,27 @@
56 49
     "zone.js": "0.8.26"
57 50
   },
58 51
   "devDependencies": {
59
-    "@capacitor/cli": "3.2.5",
60 52
     "@ionic/app-scripts": "^3.2.4",
61
-    "cordova-android": "^9.0.0",
62 53
     "cordova-ios": "^6.2.0",
63
-    "cordova-plugin-device": "^2.0.3",
64 54
     "cordova-plugin-inappbrowser": "^5.0.0",
65
-    "cordova-plugin-ionic-keyboard": "^2.0.5",
66
-    "cordova-plugin-splashscreen": "^5.0.4",
67
-    "cordova-plugin-useragent": "^1.0.6",
68
-    "cordova-plugin-whitelist": "^1.3.5",
69 55
     "typescript": "~2.6.2"
70 56
   },
71 57
   "description": "An Ionic project",
72 58
   "cordova": {
73 59
     "plugins": {
60
+      "cordova-plugin-ionic-webview": {},
74 61
       "cordova-plugin-advanced-http": {},
75
-      "cordova-plugin-x-socialsharing": {
76
-        "PHOTO_LIBRARY_ADD_USAGE_DESCRIPTION": "This app requires photo library access to function properly.",
77
-        "PHOTO_LIBRARY_USAGE_DESCRIPTION": "This app requires photo library access to function properly."
78
-      },
62
+      "cordova-plugin-x-socialsharing": {},
79 63
       "cordova-plugin-nativestorage": {},
80 64
       "cordova-plugin-ionic-keyboard": {},
81 65
       "cordova-plugin-splashscreen": {},
82 66
       "cordova-plugin-device": {},
83 67
       "cordova-plugin-whitelist": {},
84
-      "cordova-plugin-inappbrowser": {},
85
-      "im.ltdev.cordova.UserAgent": {},
86
-      "cordova-plugin-ionic-webview": {}
68
+      "cordova-plugin-inappbrowser": {}
87 69
     },
88 70
     "platforms": [
89 71
       "ios",
90 72
       "android"
91 73
     ]
92 74
   }
93
-}
75
+}

BIN
resources/icon-android.png


BIN
resources/ios/icon/icon-small.png


BIN
resources/ios/icon/icon-small@2x.png


BIN
resources/ios/icon/icon-small@3x.png


BIN
src/assets/imgs/run-event.jpg


+ 2 - 1
src/pages/detail/detail.html

@@ -49,7 +49,8 @@
49 49
         <div *ngIf="linkRec" text-center> 
50 50
             <a ion-button full [href]="linkRec" style="margin-bottom:20px; margin-top:20px">รับ Coupon</a>
51 51
         </div> -->
52
-        {{ product.cal_return }}
52
+        <!--
53
+            {{ product.cal_return }} -->
53 54
         <div *ngIf="product.cal_return > 0">
54 55
         <button ion-button full color="danger" (click)="cutPoint()" icon-start><ion-icon name="md-pricetag"></ion-icon> 
55 56
             แลกคูปอง / Redeem Coupon

+ 31 - 2
src/pages/event/event.html

@@ -7,16 +7,45 @@
7 7
 <ion-header>
8 8
 
9 9
   <ion-navbar color="primary">
10
-    <ion-title>Burn More Get More</ion-title>
10
+    <ion-title><img src="assets/imgs/ss-small.png" class='top-logo'>
11
+    </ion-title>
11 12
   </ion-navbar>
12 13
 </ion-header>
13 14
 
14 15
 
15 16
 <ion-content>
17
+    <ion-card *ngFor="let p of eventPosts">
18
+
19
+
20
+    <img [src]="p.jetpack_featured_media_url"/>
21
+
22
+  <ion-card-content>
23
+      <h2>{{ p.title.rendered }}</h2>
24
+  </ion-card-content>
25
+
26
+  <ion-row>
27
+    <ion-col>
28
+      <button ion-button icon-start color='dark' block (click)="openLink(p.guid.rendered)">
29
+        <ion-icon name="thumbs-up"></ion-icon>
30
+        Read
31
+      </button>
32
+    </ion-col>
33
+    <ion-col>
34
+      <button ion-button icon-start color='success' block>
35
+        <ion-icon name="text"></ion-icon>
36
+        <div>Join</div>
37
+      </button>
38
+    </ion-col>
39
+  </ion-row>
40
+
41
+</ion-card>
16 42
  <ion-refresher (ionRefresh)="refreshEvent($event)">
17 43
     <ion-refresher-content></ion-refresher-content>
18 44
   </ion-refresher>
19
-<event-chart></event-chart>
45
+  <!-- 
46
+  <event-chart></event-chart> -->
47
+  <!--
48
+  <img src="assets/imgs/run-event.jpg" width="100%"  /> -->
20 49
 <div>
21 50
     <ion-list-header *ngIf="allRunners" text-center>
22 51
         Total users: {{ format(allRunners.tuser + '') }} / activities: {{ format(allRunners.tact +'') }}

+ 11 - 0
src/pages/event/event.ts

@@ -44,6 +44,8 @@ export class EventPage {
44 44
     colorScheme = {
45 45
         domain: ['#5AA454', '#A10A28', '#C7B42C', '#AAAAAA']
46 46
     };
47
+    eventPosts: Array<any>;
48
+
47 49
     constructor(public navCtrl: NavController, public navParams: NavParams,
48 50
         private hp: HealthProvider,
49 51
         private ns: NativeStorage,
@@ -81,6 +83,15 @@ export class EventPage {
81 83
             .then( data => { 
82 84
                 this.po = data
83 85
             }, error => this.po = null);
86
+
87
+
88
+        this.hp.getWPPostCat(46, 1)
89
+            .then( data => {
90
+                this.eventPosts = JSON.parse(data.data);
91
+            }, error => {
92
+                console.log("errors");
93
+                console.log(""+error);
94
+            });
84 95
     }
85 96
 
86 97
     ionViewDidLoad() {

+ 2 - 2
src/pages/home/home.html

@@ -56,7 +56,7 @@
56 56
 -->
57 57
   </ion-card> 
58 58
   
59
-<ion-infinite-scroll (ionInfinite)="doInfinite($event)">
59
+<ion-infinite-scroll (ionInfinite)="doInfinite2($event)">
60 60
    <ion-infinite-scroll-content></ion-infinite-scroll-content>
61
-</ion-infinite-scroll> 
61
+</ion-infinite-scroll>  
62 62
 </ion-content>

+ 25 - 5
src/pages/home/home.ts

@@ -30,14 +30,18 @@ export class HomePage {
30 30
     }
31 31
     ionViewWillEnter() {
32 32
         console.log("view will enter");
33
-        this.healthProvider.getWPPostCat(30)
34
-            .subscribe( data => {
35
-                console.log(data);
36
-                this.wpPosts = data;
37
-            } );
38 33
     }
39 34
 
40 35
     ionViewDidEnter() {
36
+        this.healthProvider.getWPPostCat(30, this.postPage++)
37
+            .then( data => {
38
+                console.log(data);
39
+                this.wpPosts = JSON.parse(data.data);
40
+                console.log(this.wpPosts);
41
+            }, error => {
42
+                console.log("errors");
43
+                console.log(""+error);
44
+            });
41 45
         /*
42 46
         this.healthProvider.getProducts()
43 47
             .subscribe(data => { 
@@ -62,6 +66,22 @@ export class HomePage {
62 66
          this.ab.create(link, '_blank');
63 67
     }
64 68
 	
69
+	doInfinite2(infiniteScroll) {
70
+		console.log('Begin async operation');
71
+
72
+        this.healthProvider.getWPPostCat(30, this.postPage++)
73
+            .then( data => {
74
+                console.log(data);
75
+                let d = JSON.parse(data.data);
76
+				this.wpPosts = this.wpPosts.concat(d);
77
+				console.log('Async operation has ended');
78
+				infiniteScroll.complete();
79
+            }, error => {
80
+                infiniteScroll.enable(false);
81
+                this.haveMore = false;
82
+                return;	
83
+            });
84
+	}
65 85
 	doInfinite(infiniteScroll) {
66 86
 		console.log('Begin async operation');
67 87
 

+ 37 - 0
src/pages/login/login.html

@@ -69,6 +69,43 @@
69 69
                                     {{ profileObject.city }} {{ profileObject.state }} {{ profileObject.country }}<br>
70 70
                                     {{ profileObject.description }}
71 71
                                     </p>
72
+                                    <form #f="ngForm" (ngSubmit)="profileSave(f)">
73
+                                    <ion-input placeholder="Enter Your Firstname and Lastname" [(ngModel)]="profile.fullName"  required=true name="fullName" #fullName="ngModel"></ion-input>
74
+
75
+                                    <ion-label color="danger" *ngIf="fullName.invalid && (fullName.dirty || fullName.touched)">
76
+                                        <div *ngIf="fullName.errors.required">
77
+                                            Name is required.
78
+                                        </div>
79
+                                    </ion-label>
80
+                                    <ion-input placeholder="Enter Email" [(ngModel)]="profile.email" type='email' required name='email' #email="ngModel"></ion-input>
81
+
82
+                                    <ion-label color="danger" *ngIf="email.invalid && (email.dirty || email.touched)">
83
+                                        <div *ngIf="email.errors.required">
84
+                                            Email is required.
85
+                                        </div>
86
+                                    </ion-label>
87
+                                    <ion-input placeholder="Enter Tel." [(ngModel)]="profile.tel" required name='tel' #tel="ngModel"></ion-input>
88
+
89
+                                    <ion-label color="danger" *ngIf="tel.invalid && (tel.dirty || tel.touched)">
90
+                                        <div *ngIf="tel.errors.required">
91
+                                            Tel. is required.
92
+                                        </div>
93
+                                    </ion-label>
94
+                                     <ion-textarea placeholder="Enter Your Address" [(ngModel)]="profile.address" required name='address' #address="ngModel"></ion-textarea>
95
+
96
+                                    <ion-label color="danger" *ngIf="address.invalid && (address.dirty || address.touched)">
97
+                                        <div *ngIf="address.errors.required">
98
+                                            Address is required.
99
+                                        </div>
100
+                                    </ion-label>
101
+                                        <button ion-button  color="primary" name='updateProfile' type='submit' block>
102
+                                            <ion-icon name='save'></ion-icon>
103
+                                            Update Profile
104
+                                        </button>
105
+                                        <!--
106
+                                        {{ f.value | json }}
107
+                                        {{ f.valid | json }} -->
108
+                                    </form>
72 109
                                     <!--
73 110
                                     <dl>
74 111
                                         <dt>Email</dt><dd>{{ profileObject.email }}</dd>

+ 51 - 0
src/pages/login/login.ts

@@ -8,6 +8,8 @@ import { HealthProvider } from '../../providers/health/health';
8 8
 import { CouponViewPage } from '../coupon-view/coupon-view';
9 9
 import { HTTP  } from '@ionic-native/http';
10 10
 import { UserAgent } from '@ionic-native/user-agent';
11
+import {NgForm} from '@angular/forms';
12
+
11 13
 
12 14
 
13 15
 /**
@@ -17,7 +19,15 @@ import { UserAgent } from '@ionic-native/user-agent';
17 19
  * Ionic pages and navigation.
18 20
  */
19 21
 
22
+
23
+class Profile {
24
+    fullName: string;
25
+    email: string;
26
+    tel: string;
27
+    address: string;
28
+}
20 29
 //@IonicPage()
30
+
21 31
 @Component({
22 32
     selector: 'page-login',
23 33
     templateUrl: 'login.html',
@@ -35,6 +45,7 @@ export class LoginPage {
35 45
     imageURL: string;
36 46
     haveSelectAct =  false;
37 47
     haveSelectHistory =  false;
48
+    profile = new Profile();
38 49
 
39 50
     @ViewChild(Content) content: Content;
40 51
 
@@ -65,9 +76,22 @@ export class LoginPage {
65 76
                     this.hp.getMyDetail(this.token)
66 77
                         .subscribe(
67 78
                             acts => { 
79
+                        
80
+                                console.dir(acts.output.athId);
68 81
                                 console.dir(acts.output.firstname, acts.output.lastname);
69 82
                                 this.nativeStorage.setItem('profileObject', acts.output);
70 83
                                 this.profileObject = acts.output;
84
+                                this.hp.getWSProfile(this.profileObject.athId,this.token)
85
+                                    .subscribe( wsdata => {
86
+                                        console.log(JSON.stringify(wsdata));
87
+                                        this.profile.fullName = wsdata['output']['fullName'];
88
+                                        this.profile.tel = wsdata['output']['tel'];
89
+                                        this.profile.email = wsdata['output']['email'];
90
+                                        this.profile.address = wsdata['output']['address'];
91
+                                        console.log(JSON.stringify(this.profile));
92
+                                    }, error => {
93
+                                        console.log("error "+error);
94
+                                    } );
71 95
                                 this.isLogin = true;
72 96
 
73 97
                             },
@@ -142,6 +166,33 @@ export class LoginPage {
142 166
             console.log("seg ", this.seg);
143 167
         }   
144 168
     }
169
+    saveProfile() {
170
+        console.log(JSON.stringify(this.profile));
171
+    }
172
+
173
+    profileSave(f: NgForm) {
174
+        if(f.valid) {
175
+            console.log(JSON.stringify(f.value));
176
+            let profile = {
177
+                athId: this.profileObject.athId,
178
+                fullName: f.value.fullName,
179
+                email: f.value.email,
180
+                tel: f.value.tel,
181
+                address: f.value.address,
182
+                token: this.token,
183
+            };
184
+            this.hp.saveWSProfile(profile)
185
+                .subscribe(
186
+                    data => {
187
+                        console.log(data);
188
+                        alert("Your profile was updated")
189
+                    },
190
+                    error => {
191
+                        console.log(error);
192
+                    }
193
+                );
194
+        } // false
195
+    }
145 196
     getMyActivities(fromRefresher = false) {
146 197
         if( this.haveSelectAct == true )
147 198
             return;

+ 18 - 0
src/pages/register/register.html

@@ -0,0 +1,18 @@
1
+<!--
2
+  Generated template for the RegisterPage page.
3
+
4
+  See http://ionicframework.com/docs/components/#navigation for more info on
5
+  Ionic pages and navigation.
6
+-->
7
+<ion-header>
8
+
9
+  <ion-navbar>
10
+    <ion-title>register</ion-title>
11
+  </ion-navbar>
12
+
13
+</ion-header>
14
+
15
+
16
+<ion-content padding>
17
+
18
+</ion-content>

+ 13 - 0
src/pages/register/register.module.ts

@@ -0,0 +1,13 @@
1
+import { NgModule } from '@angular/core';
2
+import { IonicPageModule } from 'ionic-angular';
3
+import { RegisterPage } from './register';
4
+
5
+@NgModule({
6
+  declarations: [
7
+    RegisterPage,
8
+  ],
9
+  imports: [
10
+    IonicPageModule.forChild(RegisterPage),
11
+  ],
12
+})
13
+export class RegisterPageModule {}

+ 3 - 0
src/pages/register/register.scss

@@ -0,0 +1,3 @@
1
+page-register {
2
+
3
+}

+ 25 - 0
src/pages/register/register.ts

@@ -0,0 +1,25 @@
1
+import { Component } from '@angular/core';
2
+import { IonicPage, NavController, NavParams } from 'ionic-angular';
3
+
4
+/**
5
+ * Generated class for the RegisterPage page.
6
+ *
7
+ * See https://ionicframework.com/docs/components/#navigation for more info on
8
+ * Ionic pages and navigation.
9
+ */
10
+
11
+@IonicPage()
12
+@Component({
13
+  selector: 'page-register',
14
+  templateUrl: 'register.html',
15
+})
16
+export class RegisterPage {
17
+
18
+  constructor(public navCtrl: NavController, public navParams: NavParams) {
19
+  }
20
+
21
+  ionViewDidLoad() {
22
+    console.log('ionViewDidLoad RegisterPage');
23
+  }
24
+
25
+}

+ 12 - 4
src/providers/health/health.ts

@@ -3,6 +3,8 @@ import { Injectable } from '@angular/core';
3 3
 import { Observable } from 'rxjs';
4 4
 import { HttpHeaders } from '@angular/common/http';
5 5
 import { Device } from '@ionic-native/device';
6
+import { HTTP, HTTPResponse } from '@ionic-native/http';
7
+
6 8
 
7 9
 /*
8 10
   Generated class for the HealthProvider provider.
@@ -31,7 +33,7 @@ export class HealthProvider {
31 33
             'Authorization': 'JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoicHVibGljIiwicm9sZXMiOlsicHVibGljIl0sImV4cCI6MTczNDE2NTcxMCwiaWF0IjoxNDc0OTY1NzEwLCJuYmYiOjE0NzQ5NjU3MTAsImlkZW50aXR5IjoiNTdlOWVmMjZjMjU0ZmQ4N2Q3ZmQ4MzM2In0.-9fcm5s8qYbdqBDUX7cZJ5J3AX91fe6VrCLs_S-_eBU'
32 34
         })
33 35
     };
34
-    constructor(public http: HttpClient, private dev: Device) {
36
+    constructor(public http: HttpClient, private nhttp: HTTP, private dev: Device) {
35 37
         console.log('Hello HealthProvider Provider');
36 38
         console.log("construct device = ", this.dev.model, this.dev.serial, this.dev.isVirtual);
37 39
     }
@@ -88,9 +90,9 @@ export class HealthProvider {
88 90
         //coment
89 91
         return this.http.post(COREAPI, {action: "LIST", collection: "coredata_post", query: {active: true}, page: page});
90 92
     }
91
-    getWPPostCat(cat_no): Observable<any> {
92
-        
93
-        return this.http.get(WP + "posts?categories="+cat_no);
93
+    getWPPostCat(cat_no, page): Promise<HTTPResponse> {
94
+        console.log(WP + "posts?categories="+cat_no+"&page="+page); 
95
+        return this.nhttp.get(WP + "posts?categories="+cat_no+"&page="+page, {}, {});
94 96
     }
95 97
     recentActs(): Observable<any> {
96 98
         //coment
@@ -107,6 +109,12 @@ export class HealthProvider {
107 109
         console.log("get ath ", athId);
108 110
         return this.http.post(COREAPI, {action: "GETRUNNER", athId: athId});
109 111
     }
112
+    saveWSProfile(profile) {
113
+        return this.http.post(SV_API, {action: "UPDATE_PROFILE", athId: profile['athId'],  fullName: profile['fullName'], email: profile['email'], tel: profile['tel'], address: profile['address'], token: profile['token']});
114
+    }
115
+    getWSProfile(athId, token) {
116
+        return this.http.post(SV_API, {action: "GET_WS_PROFILE", athId: athId,   token: token });
117
+    }
110 118
     /*
111 119
   listNews(token, act): Observable<any> {
112 120
     return this.http.post("http://mcotn-api.simplico.net/api/v2.0/posts", {  action: "LIST", model: {} }, this.httpOptions);

tum/whitesports - Gogs: Simplico Git Service

Brak opisu

capabilities.php 35KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091
  1. <?php
  2. /**
  3. * Core User Role & Capabilities API
  4. *
  5. * @package WordPress
  6. * @subpackage Users
  7. */
  8. /**
  9. * Maps a capability to the primitive capabilities required of the given user to
  10. * satisfy the capability being checked.
  11. *
  12. * This function also accepts an ID of an object to map against if the capability is a meta capability. Meta
  13. * capabilities such as `edit_post` and `edit_user` are capabilities used by this function to map to primitive
  14. * capabilities that a user or role requires, such as `edit_posts` and `edit_others_posts`.
  15. *
  16. * Example usage:
  17. *
  18. * map_meta_cap( 'edit_posts', $user->ID );
  19. * map_meta_cap( 'edit_post', $user->ID, $post->ID );
  20. * map_meta_cap( 'edit_post_meta', $user->ID, $post->ID, $meta_key );
  21. *
  22. * This function does not check whether the user has the required capabilities,
  23. * it just returns what the required capabilities are.
  24. *
  25. * @since 2.0.0
  26. * @since 4.9.6 Added the `export_others_personal_data`, `erase_others_personal_data`,
  27. * and `manage_privacy_options` capabilities.
  28. * @since 5.1.0 Added the `update_php` capability.
  29. * @since 5.2.0 Added the `resume_plugin` and `resume_theme` capabilities.
  30. * @since 5.3.0 Formalized the existing and already documented `...$args` parameter
  31. * by adding it to the function signature.
  32. * @since 5.7.0 Added the `create_app_password`, `list_app_passwords`, `read_app_password`,
  33. * `edit_app_password`, `delete_app_passwords`, `delete_app_password`,
  34. * and `update_https` capabilities.
  35. *
  36. * @global array $post_type_meta_caps Used to get post type meta capabilities.
  37. *
  38. * @param string $cap Capability being checked.
  39. * @param int $user_id User ID.
  40. * @param mixed ...$args Optional further parameters, typically starting with an object ID.
  41. * @return string[] Primitive capabilities required of the user.
  42. */
  43. function map_meta_cap( $cap, $user_id, ...$args ) {
  44. $caps = array();
  45. switch ( $cap ) {
  46. case 'remove_user':
  47. // In multisite the user must be a super admin to remove themselves.
  48. if ( isset( $args[0] ) && $user_id == $args[0] && ! is_super_admin( $user_id ) ) {
  49. $caps[] = 'do_not_allow';
  50. } else {
  51. $caps[] = 'remove_users';
  52. }
  53. break;
  54. case 'promote_user':
  55. case 'add_users':
  56. $caps[] = 'promote_users';
  57. break;
  58. case 'edit_user':
  59. case 'edit_users':
  60. // Allow user to edit themselves.
  61. if ( 'edit_user' === $cap && isset( $args[0] ) && $user_id == $args[0] ) {
  62. break;
  63. }
  64. // In multisite the user must have manage_network_users caps. If editing a super admin, the user must be a super admin.
  65. if ( is_multisite() && ( ( ! is_super_admin( $user_id ) && 'edit_user' === $cap && is_super_admin( $args[0] ) ) || ! user_can( $user_id, 'manage_network_users' ) ) ) {
  66. $caps[] = 'do_not_allow';
  67. } else {
  68. $caps[] = 'edit_users'; // edit_user maps to edit_users.
  69. }
  70. break;
  71. case 'delete_post':
  72. case 'delete_page':
  73. $post = get_post( $args[0] );
  74. if ( ! $post ) {
  75. $caps[] = 'do_not_allow';
  76. break;
  77. }
  78. if ( 'revision' === $post->post_type ) {
  79. $caps[] = 'do_not_allow';
  80. break;
  81. }
  82. if ( ( get_option( 'page_for_posts' ) == $post->ID ) || ( get_option( 'page_on_front' ) == $post->ID ) ) {
  83. $caps[] = 'manage_options';
  84. break;
  85. }
  86. $post_type = get_post_type_object( $post->post_type );
  87. if ( ! $post_type ) {
  88. /* translators: 1: Post type, 2: Capability name. */
  89. _doing_it_wrong( __FUNCTION__, sprintf( __( 'The post type %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post of that type.' ), $post->post_type, $cap ), '4.4.0' );
  90. $caps[] = 'edit_others_posts';
  91. break;
  92. }
  93. if ( ! $post_type->map_meta_cap ) {
  94. $caps[] = $post_type->cap->$cap;
  95. // Prior to 3.1 we would re-call map_meta_cap here.
  96. if ( 'delete_post' === $cap ) {
  97. $cap = $post_type->cap->$cap;
  98. }
  99. break;
  100. }
  101. // If the post author is set and the user is the author...
  102. if ( $post->post_author && $user_id == $post->post_author ) {
  103. // If the post is published or scheduled...
  104. if ( in_array( $post->post_status, array( 'publish', 'future' ), true ) ) {
  105. $caps[] = $post_type->cap->delete_published_posts;
  106. } elseif ( 'trash' === $post->post_status ) {
  107. $status = get_post_meta( $post->ID, '_wp_trash_meta_status', true );
  108. if ( in_array( $status, array( 'publish', 'future' ), true ) ) {
  109. $caps[] = $post_type->cap->delete_published_posts;
  110. } else {
  111. $caps[] = $post_type->cap->delete_posts;
  112. }
  113. } else {
  114. // If the post is draft...
  115. $caps[] = $post_type->cap->delete_posts;
  116. }
  117. } else {
  118. // The user is trying to edit someone else's post.
  119. $caps[] = $post_type->cap->delete_others_posts;
  120. // The post is published or scheduled, extra cap required.
  121. if ( in_array( $post->post_status, array( 'publish', 'future' ), true ) ) {
  122. $caps[] = $post_type->cap->delete_published_posts;
  123. } elseif ( 'private' === $post->post_status ) {
  124. $caps[] = $post_type->cap->delete_private_posts;
  125. }
  126. }
  127. /*
  128. * Setting the privacy policy page requires `manage_privacy_options`,
  129. * so deleting it should require that too.
  130. */
  131. if ( (int) get_option( 'wp_page_for_privacy_policy' ) === $post->ID ) {
  132. $caps = array_merge( $caps, map_meta_cap( 'manage_privacy_options', $user_id ) );
  133. }
  134. break;
  135. // edit_post breaks down to edit_posts, edit_published_posts, or
  136. // edit_others_posts.
  137. case 'edit_post':
  138. case 'edit_page':
  139. $post = get_post( $args[0] );
  140. if ( ! $post ) {
  141. $caps[] = 'do_not_allow';
  142. break;
  143. }
  144. if ( 'revision' === $post->post_type ) {
  145. $post = get_post( $post->post_parent );
  146. if ( ! $post ) {
  147. $caps[] = 'do_not_allow';
  148. break;
  149. }
  150. }
  151. $post_type = get_post_type_object( $post->post_type );
  152. if ( ! $post_type ) {
  153. /* translators: 1: Post type, 2: Capability name. */
  154. _doing_it_wrong( __FUNCTION__, sprintf( __( 'The post type %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post of that type.' ), $post->post_type, $cap ), '4.4.0' );
  155. $caps[] = 'edit_others_posts';
  156. break;
  157. }
  158. if ( ! $post_type->map_meta_cap ) {
  159. $caps[] = $post_type->cap->$cap;
  160. // Prior to 3.1 we would re-call map_meta_cap here.
  161. if ( 'edit_post' === $cap ) {
  162. $cap = $post_type->cap->$cap;
  163. }
  164. break;
  165. }
  166. // If the post author is set and the user is the author...
  167. if ( $post->post_author && $user_id == $post->post_author ) {
  168. // If the post is published or scheduled...
  169. if ( in_array( $post->post_status, array( 'publish', 'future' ), true ) ) {
  170. $caps[] = $post_type->cap->edit_published_posts;
  171. } elseif ( 'trash' === $post->post_status ) {
  172. $status = get_post_meta( $post->ID, '_wp_trash_meta_status', true );
  173. if ( in_array( $status, array( 'publish', 'future' ), true ) ) {
  174. $caps[] = $post_type->cap->edit_published_posts;
  175. } else {
  176. $caps[] = $post_type->cap->edit_posts;
  177. }
  178. } else {
  179. // If the post is draft...
  180. $caps[] = $post_type->cap->edit_posts;
  181. }
  182. } else {
  183. // The user is trying to edit someone else's post.
  184. $caps[] = $post_type->cap->edit_others_posts;
  185. // The post is published or scheduled, extra cap required.
  186. if ( in_array( $post->post_status, array( 'publish', 'future' ), true ) ) {
  187. $caps[] = $post_type->cap->edit_published_posts;
  188. } elseif ( 'private' === $post->post_status ) {
  189. $caps[] = $post_type->cap->edit_private_posts;
  190. }
  191. }
  192. /*
  193. * Setting the privacy policy page requires `manage_privacy_options`,
  194. * so editing it should require that too.
  195. */
  196. if ( (int) get_option( 'wp_page_for_privacy_policy' ) === $post->ID ) {
  197. $caps = array_merge( $caps, map_meta_cap( 'manage_privacy_options', $user_id ) );
  198. }
  199. break;
  200. case 'read_post':
  201. case 'read_page':
  202. $post = get_post( $args[0] );
  203. if ( ! $post ) {
  204. $caps[] = 'do_not_allow';
  205. break;
  206. }
  207. if ( 'revision' === $post->post_type ) {
  208. $post = get_post( $post->post_parent );
  209. if ( ! $post ) {
  210. $caps[] = 'do_not_allow';
  211. break;
  212. }
  213. }
  214. $post_type = get_post_type_object( $post->post_type );
  215. if ( ! $post_type ) {
  216. /* translators: 1: Post type, 2: Capability name. */
  217. _doing_it_wrong( __FUNCTION__, sprintf( __( 'The post type %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post of that type.' ), $post->post_type, $cap ), '4.4.0' );
  218. $caps[] = 'edit_others_posts';
  219. break;
  220. }
  221. if ( ! $post_type->map_meta_cap ) {
  222. $caps[] = $post_type->cap->$cap;
  223. // Prior to 3.1 we would re-call map_meta_cap here.
  224. if ( 'read_post' === $cap ) {
  225. $cap = $post_type->cap->$cap;
  226. }
  227. break;
  228. }
  229. $status_obj = get_post_status_object( get_post_status( $post ) );
  230. if ( ! $status_obj ) {
  231. /* translators: 1: Post status, 2: Capability name. */
  232. _doing_it_wrong( __FUNCTION__, sprintf( __( 'The post status %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post with that status.' ), get_post_status( $post ), $cap ), '5.4.0' );
  233. $caps[] = 'edit_others_posts';
  234. break;
  235. }
  236. if ( $status_obj->public ) {
  237. $caps[] = $post_type->cap->read;
  238. break;
  239. }
  240. if ( $post->post_author && $user_id == $post->post_author ) {
  241. $caps[] = $post_type->cap->read;
  242. } elseif ( $status_obj->private ) {
  243. $caps[] = $post_type->cap->read_private_posts;
  244. } else {
  245. $caps = map_meta_cap( 'edit_post', $user_id, $post->ID );
  246. }
  247. break;
  248. case 'publish_post':
  249. $post = get_post( $args[0] );
  250. if ( ! $post ) {
  251. $caps[] = 'do_not_allow';
  252. break;
  253. }
  254. $post_type = get_post_type_object( $post->post_type );
  255. if ( ! $post_type ) {
  256. /* translators: 1: Post type, 2: Capability name. */
  257. _doing_it_wrong( __FUNCTION__, sprintf( __( 'The post type %1$s is not registered, so it may not be reliable to check the capability "%2$s" against a post of that type.' ), $post->post_type, $cap ), '4.4.0' );
  258. $caps[] = 'edit_others_posts';
  259. break;
  260. }
  261. $caps[] = $post_type->cap->publish_posts;
  262. break;
  263. case 'edit_post_meta':
  264. case 'delete_post_meta':
  265. case 'add_post_meta':
  266. case 'edit_comment_meta':
  267. case 'delete_comment_meta':
  268. case 'add_comment_meta':
  269. case 'edit_term_meta':
  270. case 'delete_term_meta':
  271. case 'add_term_meta':
  272. case 'edit_user_meta':
  273. case 'delete_user_meta':
  274. case 'add_user_meta':
  275. $object_type = explode( '_', $cap )[1];
  276. $object_id = (int) $args[0];
  277. $object_subtype = get_object_subtype( $object_type, $object_id );
  278. if ( empty( $object_subtype ) ) {
  279. $caps[] = 'do_not_allow';
  280. break;
  281. }
  282. $caps = map_meta_cap( "edit_{$object_type}", $user_id, $object_id );
  283. $meta_key = isset( $args[1] ) ? $args[1] : false;
  284. if ( $meta_key ) {
  285. $allowed = ! is_protected_meta( $meta_key, $object_type );
  286. if ( ! empty( $object_subtype ) && has_filter( "auth_{$object_type}_meta_{$meta_key}_for_{$object_subtype}" ) ) {
  287. /**
  288. * Filters whether the user is allowed to edit a specific meta key of a specific object type and subtype.
  289. *
  290. * The dynamic portions of the hook name, `$object_type`, `$meta_key`,
  291. * and `$object_subtype`, refer to the metadata object type (comment, post, term or user),
  292. * the meta key value, and the object subtype respectively.
  293. *
  294. * @since 4.9.8
  295. *
  296. * @param bool $allowed Whether the user can add the object meta. Default false.
  297. * @param string $meta_key The meta key.
  298. * @param int $object_id Object ID.
  299. * @param int $user_id User ID.
  300. * @param string $cap Capability name.
  301. * @param string[] $caps Array of the user's capabilities.
  302. */
  303. $allowed = apply_filters( "auth_{$object_type}_meta_{$meta_key}_for_{$object_subtype}", $allowed, $meta_key, $object_id, $user_id, $cap, $caps );
  304. } else {
  305. /**
  306. * Filters whether the user is allowed to edit a specific meta key of a specific object type.
  307. *
  308. * Return true to have the mapped meta caps from `edit_{$object_type}` apply.
  309. *
  310. * The dynamic portion of the hook name, `$object_type` refers to the object type being filtered.
  311. * The dynamic portion of the hook name, `$meta_key`, refers to the meta key passed to map_meta_cap().
  312. *
  313. * @since 3.3.0 As `auth_post_meta_{$meta_key}`.
  314. * @since 4.6.0
  315. *
  316. * @param bool $allowed Whether the user can add the object meta. Default false.
  317. * @param string $meta_key The meta key.
  318. * @param int $object_id Object ID.
  319. * @param int $user_id User ID.
  320. * @param string $cap Capability name.
  321. * @param string[] $caps Array of the user's capabilities.
  322. */
  323. $allowed = apply_filters( "auth_{$object_type}_meta_{$meta_key}", $allowed, $meta_key, $object_id, $user_id, $cap, $caps );
  324. }
  325. if ( ! empty( $object_subtype ) ) {
  326. /**
  327. * Filters whether the user is allowed to edit meta for specific object types/subtypes.
  328. *
  329. * Return true to have the mapped meta caps from `edit_{$object_type}` apply.
  330. *
  331. * The dynamic portion of the hook name, `$object_type` refers to the object type being filtered.
  332. * The dynamic portion of the hook name, `$object_subtype` refers to the object subtype being filtered.
  333. * The dynamic portion of the hook name, `$meta_key`, refers to the meta key passed to map_meta_cap().
  334. *
  335. * @since 4.6.0 As `auth_post_{$post_type}_meta_{$meta_key}`.
  336. * @since 4.7.0 Renamed from `auth_post_{$post_type}_meta_{$meta_key}` to
  337. * `auth_{$object_type}_{$object_subtype}_meta_{$meta_key}`.
  338. * @deprecated 4.9.8 Use {@see 'auth_{$object_type}_meta_{$meta_key}_for_{$object_subtype}'} instead.
  339. *
  340. * @param bool $allowed Whether the user can add the object meta. Default false.
  341. * @param string $meta_key The meta key.
  342. * @param int $object_id Object ID.
  343. * @param int $user_id User ID.
  344. * @param string $cap Capability name.
  345. * @param string[] $caps Array of the user's capabilities.
  346. */
  347. $allowed = apply_filters_deprecated(
  348. "auth_{$object_type}_{$object_subtype}_meta_{$meta_key}",
  349. array( $allowed, $meta_key, $object_id, $user_id, $cap, $caps ),
  350. '4.9.8',
  351. "auth_{$object_type}_meta_{$meta_key}_for_{$object_subtype}"
  352. );
  353. }
  354. if ( ! $allowed ) {
  355. $caps[] = $cap;
  356. }
  357. }
  358. break;
  359. case 'edit_comment':
  360. $comment = get_comment( $args[0] );
  361. if ( ! $comment ) {
  362. $caps[] = 'do_not_allow';
  363. break;
  364. }
  365. $post = get_post( $comment->comment_post_ID );
  366. /*
  367. * If the post doesn't exist, we have an orphaned comment.
  368. * Fall back to the edit_posts capability, instead.
  369. */
  370. if ( $post ) {
  371. $caps = map_meta_cap( 'edit_post', $user_id, $post->ID );
  372. } else {
  373. $caps = map_meta_cap( 'edit_posts', $user_id );
  374. }
  375. break;
  376. case 'unfiltered_upload':
  377. if ( defined( 'ALLOW_UNFILTERED_UPLOADS' ) && ALLOW_UNFILTERED_UPLOADS && ( ! is_multisite() || is_super_admin( $user_id ) ) ) {
  378. $caps[] = $cap;
  379. } else {
  380. $caps[] = 'do_not_allow';
  381. }
  382. break;
  383. case 'edit_css':
  384. case 'unfiltered_html':
  385. // Disallow unfiltered_html for all users, even admins and super admins.
  386. if ( defined( 'DISALLOW_UNFILTERED_HTML' ) && DISALLOW_UNFILTERED_HTML ) {
  387. $caps[] = 'do_not_allow';
  388. } elseif ( is_multisite() && ! is_super_admin( $user_id ) ) {
  389. $caps[] = 'do_not_allow';
  390. } else {
  391. $caps[] = 'unfiltered_html';
  392. }
  393. break;
  394. case 'edit_files':
  395. case 'edit_plugins':
  396. case 'edit_themes':
  397. // Disallow the file editors.
  398. if ( defined( 'DISALLOW_FILE_EDIT' ) && DISALLOW_FILE_EDIT ) {
  399. $caps[] = 'do_not_allow';
  400. } elseif ( ! wp_is_file_mod_allowed( 'capability_edit_themes' ) ) {
  401. $caps[] = 'do_not_allow';
  402. } elseif ( is_multisite() && ! is_super_admin( $user_id ) ) {
  403. $caps[] = 'do_not_allow';
  404. } else {
  405. $caps[] = $cap;
  406. }
  407. break;
  408. case 'update_plugins':
  409. case 'delete_plugins':
  410. case 'install_plugins':
  411. case 'upload_plugins':
  412. case 'update_themes':
  413. case 'delete_themes':
  414. case 'install_themes':
  415. case 'upload_themes':
  416. case 'update_core':
  417. // Disallow anything that creates, deletes, or updates core, plugin, or theme files.
  418. // Files in uploads are excepted.
  419. if ( ! wp_is_file_mod_allowed( 'capability_update_core' ) ) {
  420. $caps[] = 'do_not_allow';
  421. } elseif ( is_multisite() && ! is_super_admin( $user_id ) ) {
  422. $caps[] = 'do_not_allow';
  423. } elseif ( 'upload_themes' === $cap ) {
  424. $caps[] = 'install_themes';
  425. } elseif ( 'upload_plugins' === $cap ) {
  426. $caps[] = 'install_plugins';
  427. } else {
  428. $caps[] = $cap;
  429. }
  430. break;
  431. case 'install_languages':
  432. case 'update_languages':
  433. if ( ! wp_is_file_mod_allowed( 'can_install_language_pack' ) ) {
  434. $caps[] = 'do_not_allow';
  435. } elseif ( is_multisite() && ! is_super_admin( $user_id ) ) {
  436. $caps[] = 'do_not_allow';
  437. } else {
  438. $caps[] = 'install_languages';
  439. }
  440. break;
  441. case 'activate_plugins':
  442. case 'deactivate_plugins':
  443. case 'activate_plugin':
  444. case 'deactivate_plugin':
  445. $caps[] = 'activate_plugins';
  446. if ( is_multisite() ) {
  447. // update_, install_, and delete_ are handled above with is_super_admin().
  448. $menu_perms = get_site_option( 'menu_items', array() );
  449. if ( empty( $menu_perms['plugins'] ) ) {
  450. $caps[] = 'manage_network_plugins';
  451. }
  452. }
  453. break;
  454. case 'resume_plugin':
  455. $caps[] = 'resume_plugins';
  456. break;
  457. case 'resume_theme':
  458. $caps[] = 'resume_themes';
  459. break;
  460. case 'delete_user':
  461. case 'delete_users':
  462. // If multisite only super admins can delete users.
  463. if ( is_multisite() && ! is_super_admin( $user_id ) ) {
  464. $caps[] = 'do_not_allow';
  465. } else {
  466. $caps[] = 'delete_users'; // delete_user maps to delete_users.
  467. }
  468. break;
  469. case 'create_users':
  470. if ( ! is_multisite() ) {
  471. $caps[] = $cap;
  472. } elseif ( is_super_admin( $user_id ) || get_site_option( 'add_new_users' ) ) {
  473. $caps[] = $cap;
  474. } else {
  475. $caps[] = 'do_not_allow';
  476. }
  477. break;
  478. case 'manage_links':
  479. if ( get_option( 'link_manager_enabled' ) ) {
  480. $caps[] = $cap;
  481. } else {
  482. $caps[] = 'do_not_allow';
  483. }
  484. break;
  485. case 'customize':
  486. $caps[] = 'edit_theme_options';
  487. break;
  488. case 'delete_site':
  489. if ( is_multisite() ) {
  490. $caps[] = 'manage_options';
  491. } else {
  492. $caps[] = 'do_not_allow';
  493. }
  494. break;
  495. case 'edit_term':
  496. case 'delete_term':
  497. case 'assign_term':
  498. $term_id = (int) $args[0];
  499. $term = get_term( $term_id );
  500. if ( ! $term || is_wp_error( $term ) ) {
  501. $caps[] = 'do_not_allow';
  502. break;
  503. }
  504. $tax = get_taxonomy( $term->taxonomy );
  505. if ( ! $tax ) {
  506. $caps[] = 'do_not_allow';
  507. break;
  508. }
  509. if ( 'delete_term' === $cap
  510. && ( get_option( 'default_' . $term->taxonomy ) == $term->term_id
  511. || get_option( 'default_term_' . $term->taxonomy ) == $term->term_id )
  512. ) {
  513. $caps[] = 'do_not_allow';
  514. break;
  515. }
  516. $taxo_cap = $cap . 's';
  517. $caps = map_meta_cap( $tax->cap->$taxo_cap, $user_id, $term_id );
  518. break;
  519. case 'manage_post_tags':
  520. case 'edit_categories':
  521. case 'edit_post_tags':
  522. case 'delete_categories':
  523. case 'delete_post_tags':
  524. $caps[] = 'manage_categories';
  525. break;
  526. case 'assign_categories':
  527. case 'assign_post_tags':
  528. $caps[] = 'edit_posts';
  529. break;
  530. case 'create_sites':
  531. case 'delete_sites':
  532. case 'manage_network':
  533. case 'manage_sites':
  534. case 'manage_network_users':
  535. case 'manage_network_plugins':
  536. case 'manage_network_themes':
  537. case 'manage_network_options':
  538. case 'upgrade_network':
  539. $caps[] = $cap;
  540. break;
  541. case 'setup_network':
  542. if ( is_multisite() ) {
  543. $caps[] = 'manage_network_options';
  544. } else {
  545. $caps[] = 'manage_options';
  546. }
  547. break;
  548. case 'update_php':
  549. if ( is_multisite() && ! is_super_admin( $user_id ) ) {
  550. $caps[] = 'do_not_allow';
  551. } else {
  552. $caps[] = 'update_core';
  553. }
  554. break;
  555. case 'update_https':
  556. if ( is_multisite() && ! is_super_admin( $user_id ) ) {
  557. $caps[] = 'do_not_allow';
  558. } else {
  559. $caps[] = 'manage_options';
  560. $caps[] = 'update_core';
  561. }
  562. break;
  563. case 'export_others_personal_data':
  564. case 'erase_others_personal_data':
  565. case 'manage_privacy_options':
  566. $caps[] = is_multisite() ? 'manage_network' : 'manage_options';
  567. break;
  568. case 'create_app_password':
  569. case 'list_app_passwords':
  570. case 'read_app_password':
  571. case 'edit_app_password':
  572. case 'delete_app_passwords':
  573. case 'delete_app_password':
  574. $caps = map_meta_cap( 'edit_user', $user_id, $args[0] );
  575. break;
  576. default:
  577. // Handle meta capabilities for custom post types.
  578. global $post_type_meta_caps;
  579. if ( isset( $post_type_meta_caps[ $cap ] ) ) {
  580. return map_meta_cap( $post_type_meta_caps[ $cap ], $user_id, ...$args );
  581. }
  582. // Block capabilities map to their post equivalent.
  583. $block_caps = array(
  584. 'edit_blocks',
  585. 'edit_others_blocks',
  586. 'publish_blocks',
  587. 'read_private_blocks',
  588. 'delete_blocks',
  589. 'delete_private_blocks',
  590. 'delete_published_blocks',
  591. 'delete_others_blocks',
  592. 'edit_private_blocks',
  593. 'edit_published_blocks',
  594. );
  595. if ( in_array( $cap, $block_caps, true ) ) {
  596. $cap = str_replace( '_blocks', '_posts', $cap );
  597. }
  598. // If no meta caps match, return the original cap.
  599. $caps[] = $cap;
  600. }
  601. /**
  602. * Filters the primitive capabilities required of the given user to satisfy the
  603. * capability being checked.
  604. *
  605. * @since 2.8.0
  606. *
  607. * @param string[] $caps Primitive capabilities required of the user.
  608. * @param string $cap Capability being checked.
  609. * @param int $user_id The user ID.
  610. * @param array $args Adds context to the capability check, typically
  611. * starting with an object ID.
  612. */
  613. return apply_filters( 'map_meta_cap', $caps, $cap, $user_id, $args );
  614. }
  615. /**
  616. * Returns whether the current user has the specified capability.
  617. *
  618. * This function also accepts an ID of an object to check against if the capability is a meta capability. Meta
  619. * capabilities such as `edit_post` and `edit_user` are capabilities used by the `map_meta_cap()` function to
  620. * map to primitive capabilities that a user or role has, such as `edit_posts` and `edit_others_posts`.
  621. *
  622. * Example usage:
  623. *
  624. * current_user_can( 'edit_posts' );
  625. * current_user_can( 'edit_post', $post->ID );
  626. * current_user_can( 'edit_post_meta', $post->ID, $meta_key );
  627. *
  628. * While checking against particular roles in place of a capability is supported
  629. * in part, this practice is discouraged as it may produce unreliable results.
  630. *
  631. * Note: Will always return true if the current user is a super admin, unless specifically denied.
  632. *
  633. * @since 2.0.0
  634. * @since 5.3.0 Formalized the existing and already documented `...$args` parameter
  635. * by adding it to the function signature.
  636. * @since 5.8.0 Converted to wrapper for the user_can() function.
  637. *
  638. * @see WP_User::has_cap()
  639. * @see map_meta_cap()
  640. *
  641. * @param string $capability Capability name.
  642. * @param mixed ...$args Optional further parameters, typically starting with an object ID.
  643. * @return bool Whether the current user has the given capability. If `$capability` is a meta cap and `$object_id` is
  644. * passed, whether the current user has the given meta capability for the given object.
  645. */
  646. function current_user_can( $capability, ...$args ) {
  647. return user_can( wp_get_current_user(), $capability, ...$args );
  648. }
  649. /**
  650. * Returns whether the current user has the specified capability for a given site.
  651. *
  652. * This function also accepts an ID of an object to check against if the capability is a meta capability. Meta
  653. * capabilities such as `edit_post` and `edit_user` are capabilities used by the `map_meta_cap()` function to
  654. * map to primitive capabilities that a user or role has, such as `edit_posts` and `edit_others_posts`.
  655. *
  656. * Example usage:
  657. *
  658. * current_user_can_for_blog( $blog_id, 'edit_posts' );
  659. * current_user_can_for_blog( $blog_id, 'edit_post', $post->ID );
  660. * current_user_can_for_blog( $blog_id, 'edit_post_meta', $post->ID, $meta_key );
  661. *
  662. * @since 3.0.0
  663. * @since 5.3.0 Formalized the existing and already documented `...$args` parameter
  664. * by adding it to the function signature.
  665. * @since 5.8.0 Wraps current_user_can() after switching to blog.
  666. *
  667. * @param int $blog_id Site ID.
  668. * @param string $capability Capability name.
  669. * @param mixed ...$args Optional further parameters, typically starting with an object ID.
  670. * @return bool Whether the user has the given capability.
  671. */
  672. function current_user_can_for_blog( $blog_id, $capability, ...$args ) {
  673. $switched = is_multisite() ? switch_to_blog( $blog_id ) : false;
  674. $can = current_user_can( $capability, ...$args );
  675. if ( $switched ) {
  676. restore_current_blog();
  677. }
  678. return $can;
  679. }
  680. /**
  681. * Returns whether the author of the supplied post has the specified capability.
  682. *
  683. * This function also accepts an ID of an object to check against if the capability is a meta capability. Meta
  684. * capabilities such as `edit_post` and `edit_user` are capabilities used by the `map_meta_cap()` function to
  685. * map to primitive capabilities that a user or role has, such as `edit_posts` and `edit_others_posts`.
  686. *
  687. * Example usage:
  688. *
  689. * author_can( $post, 'edit_posts' );
  690. * author_can( $post, 'edit_post', $post->ID );
  691. * author_can( $post, 'edit_post_meta', $post->ID, $meta_key );
  692. *
  693. * @since 2.9.0
  694. * @since 5.3.0 Formalized the existing and already documented `...$args` parameter
  695. * by adding it to the function signature.
  696. *
  697. * @param int|WP_Post $post Post ID or post object.
  698. * @param string $capability Capability name.
  699. * @param mixed ...$args Optional further parameters, typically starting with an object ID.
  700. * @return bool Whether the post author has the given capability.
  701. */
  702. function author_can( $post, $capability, ...$args ) {
  703. $post = get_post( $post );
  704. if ( ! $post ) {
  705. return false;
  706. }
  707. $author = get_userdata( $post->post_author );
  708. if ( ! $author ) {
  709. return false;
  710. }
  711. return $author->has_cap( $capability, ...$args );
  712. }
  713. /**
  714. * Returns whether a particular user has the specified capability.
  715. *
  716. * This function also accepts an ID of an object to check against if the capability is a meta capability. Meta
  717. * capabilities such as `edit_post` and `edit_user` are capabilities used by the `map_meta_cap()` function to
  718. * map to primitive capabilities that a user or role has, such as `edit_posts` and `edit_others_posts`.
  719. *
  720. * Example usage:
  721. *
  722. * user_can( $user->ID, 'edit_posts' );
  723. * user_can( $user->ID, 'edit_post', $post->ID );
  724. * user_can( $user->ID, 'edit_post_meta', $post->ID, $meta_key );
  725. *
  726. * @since 3.1.0
  727. * @since 5.3.0 Formalized the existing and already documented `...$args` parameter
  728. * by adding it to the function signature.
  729. *
  730. * @param int|WP_User $user User ID or object.
  731. * @param string $capability Capability name.
  732. * @param mixed ...$args Optional further parameters, typically starting with an object ID.
  733. * @return bool Whether the user has the given capability.
  734. */
  735. function user_can( $user, $capability, ...$args ) {
  736. if ( ! is_object( $user ) ) {
  737. $user = get_userdata( $user );
  738. }
  739. if ( empty( $user ) ) {
  740. // User is logged out, create anonymous user object.
  741. $user = new WP_User( 0 );
  742. $user->init( new stdClass );
  743. }
  744. return $user->has_cap( $capability, ...$args );
  745. }
  746. /**
  747. * Retrieves the global WP_Roles instance and instantiates it if necessary.
  748. *
  749. * @since 4.3.0
  750. *
  751. * @global WP_Roles $wp_roles WordPress role management object.
  752. *
  753. * @return WP_Roles WP_Roles global instance if not already instantiated.
  754. */
  755. function wp_roles() {
  756. global $wp_roles;
  757. if ( ! isset( $wp_roles ) ) {
  758. $wp_roles = new WP_Roles();
  759. }
  760. return $wp_roles;
  761. }
  762. /**
  763. * Retrieves role object.
  764. *
  765. * @since 2.0.0
  766. *
  767. * @param string $role Role name.
  768. * @return WP_Role|null WP_Role object if found, null if the role does not exist.
  769. */
  770. function get_role( $role ) {
  771. return wp_roles()->get_role( $role );
  772. }
  773. /**
  774. * Adds a role, if it does not exist.
  775. *
  776. * @since 2.0.0
  777. *
  778. * @param string $role Role name.
  779. * @param string $display_name Display name for role.
  780. * @param bool[] $capabilities List of capabilities keyed by the capability name,
  781. * e.g. array( 'edit_posts' => true, 'delete_posts' => false ).
  782. * @return WP_Role|null WP_Role object if role is added, null if already exists.
  783. */
  784. function add_role( $role, $display_name, $capabilities = array() ) {
  785. if ( empty( $role ) ) {
  786. return;
  787. }
  788. return wp_roles()->add_role( $role, $display_name, $capabilities );
  789. }
  790. /**
  791. * Removes a role, if it exists.
  792. *
  793. * @since 2.0.0
  794. *
  795. * @param string $role Role name.
  796. */
  797. function remove_role( $role ) {
  798. wp_roles()->remove_role( $role );
  799. }
  800. /**
  801. * Retrieves a list of super admins.
  802. *
  803. * @since 3.0.0
  804. *
  805. * @global array $super_admins
  806. *
  807. * @return string[] List of super admin logins.
  808. */
  809. function get_super_admins() {
  810. global $super_admins;
  811. if ( isset( $super_admins ) ) {
  812. return $super_admins;
  813. } else {
  814. return get_site_option( 'site_admins', array( 'admin' ) );
  815. }
  816. }
  817. /**
  818. * Determines whether user is a site admin.
  819. *
  820. * @since 3.0.0
  821. *
  822. * @param int|false $user_id Optional. The ID of a user. Defaults to false, to check the current user.
  823. * @return bool Whether the user is a site admin.
  824. */
  825. function is_super_admin( $user_id = false ) {
  826. if ( ! $user_id ) {
  827. $user = wp_get_current_user();
  828. } else {
  829. $user = get_userdata( $user_id );
  830. }
  831. if ( ! $user || ! $user->exists() ) {
  832. return false;
  833. }
  834. if ( is_multisite() ) {
  835. $super_admins = get_super_admins();
  836. if ( is_array( $super_admins ) && in_array( $user->user_login, $super_admins, true ) ) {
  837. return true;
  838. }
  839. } else {
  840. if ( $user->has_cap( 'delete_users' ) ) {
  841. return true;
  842. }
  843. }
  844. return false;
  845. }
  846. /**
  847. * Grants Super Admin privileges.
  848. *
  849. * @since 3.0.0
  850. *
  851. * @global array $super_admins
  852. *
  853. * @param int $user_id ID of the user to be granted Super Admin privileges.
  854. * @return bool True on success, false on failure. This can fail when the user is
  855. * already a super admin or when the `$super_admins` global is defined.
  856. */
  857. function grant_super_admin( $user_id ) {
  858. // If global super_admins override is defined, there is nothing to do here.
  859. if ( isset( $GLOBALS['super_admins'] ) || ! is_multisite() ) {
  860. return false;
  861. }
  862. /**
  863. * Fires before the user is granted Super Admin privileges.
  864. *
  865. * @since 3.0.0
  866. *
  867. * @param int $user_id ID of the user that is about to be granted Super Admin privileges.
  868. */
  869. do_action( 'grant_super_admin', $user_id );
  870. // Directly fetch site_admins instead of using get_super_admins().
  871. $super_admins = get_site_option( 'site_admins', array( 'admin' ) );
  872. $user = get_userdata( $user_id );
  873. if ( $user && ! in_array( $user->user_login, $super_admins, true ) ) {
  874. $super_admins[] = $user->user_login;
  875. update_site_option( 'site_admins', $super_admins );
  876. /**
  877. * Fires after the user is granted Super Admin privileges.
  878. *
  879. * @since 3.0.0
  880. *
  881. * @param int $user_id ID of the user that was granted Super Admin privileges.
  882. */
  883. do_action( 'granted_super_admin', $user_id );
  884. return true;
  885. }
  886. return false;
  887. }
  888. /**
  889. * Revokes Super Admin privileges.
  890. *
  891. * @since 3.0.0
  892. *
  893. * @global array $super_admins
  894. *
  895. * @param int $user_id ID of the user Super Admin privileges to be revoked from.
  896. * @return bool True on success, false on failure. This can fail when the user's email
  897. * is the network admin email or when the `$super_admins` global is defined.
  898. */
  899. function revoke_super_admin( $user_id ) {
  900. // If global super_admins override is defined, there is nothing to do here.
  901. if ( isset( $GLOBALS['super_admins'] ) || ! is_multisite() ) {
  902. return false;
  903. }
  904. /**
  905. * Fires before the user's Super Admin privileges are revoked.
  906. *
  907. * @since 3.0.0
  908. *
  909. * @param int $user_id ID of the user Super Admin privileges are being revoked from.
  910. */
  911. do_action( 'revoke_super_admin', $user_id );
  912. // Directly fetch site_admins instead of using get_super_admins().
  913. $super_admins = get_site_option( 'site_admins', array( 'admin' ) );
  914. $user = get_userdata( $user_id );
  915. if ( $user && 0 !== strcasecmp( $user->user_email, get_site_option( 'admin_email' ) ) ) {
  916. $key = array_search( $user->user_login, $super_admins, true );
  917. if ( false !== $key ) {
  918. unset( $super_admins[ $key ] );
  919. update_site_option( 'site_admins', $super_admins );
  920. /**
  921. * Fires after the user's Super Admin privileges are revoked.
  922. *
  923. * @since 3.0.0
  924. *
  925. * @param int $user_id ID of the user Super Admin privileges were revoked from.
  926. */
  927. do_action( 'revoked_super_admin', $user_id );
  928. return true;
  929. }
  930. }
  931. return false;
  932. }
  933. /**
  934. * Filters the user capabilities to grant the 'install_languages' capability as necessary.
  935. *
  936. * A user must have at least one out of the 'update_core', 'install_plugins', and
  937. * 'install_themes' capabilities to qualify for 'install_languages'.
  938. *
  939. * @since 4.9.0
  940. *
  941. * @param bool[] $allcaps An array of all the user's capabilities.
  942. * @return bool[] Filtered array of the user's capabilities.
  943. */
  944. function wp_maybe_grant_install_languages_cap( $allcaps ) {
  945. if ( ! empty( $allcaps['update_core'] ) || ! empty( $allcaps['install_plugins'] ) || ! empty( $allcaps['install_themes'] ) ) {
  946. $allcaps['install_languages'] = true;
  947. }
  948. return $allcaps;
  949. }
  950. /**
  951. * Filters the user capabilities to grant the 'resume_plugins' and 'resume_themes' capabilities as necessary.
  952. *
  953. * @since 5.2.0
  954. *
  955. * @param bool[] $allcaps An array of all the user's capabilities.
  956. * @return bool[] Filtered array of the user's capabilities.
  957. */
  958. function wp_maybe_grant_resume_extensions_caps( $allcaps ) {
  959. // Even in a multisite, regular administrators should be able to resume plugins.
  960. if ( ! empty( $allcaps['activate_plugins'] ) ) {
  961. $allcaps['resume_plugins'] = true;
  962. }
  963. // Even in a multisite, regular administrators should be able to resume themes.
  964. if ( ! empty( $allcaps['switch_themes'] ) ) {
  965. $allcaps['resume_themes'] = true;
  966. }
  967. return $allcaps;
  968. }
  969. /**
  970. * Filters the user capabilities to grant the 'view_site_health_checks' capabilities as necessary.
  971. *
  972. * @since 5.2.2
  973. *
  974. * @param bool[] $allcaps An array of all the user's capabilities.
  975. * @param string[] $caps Required primitive capabilities for the requested capability.
  976. * @param array $args {
  977. * Arguments that accompany the requested capability check.
  978. *
  979. * @type string $0 Requested capability.
  980. * @type int $1 Concerned user ID.
  981. * @type mixed ...$2 Optional second and further parameters, typically object ID.
  982. * }
  983. * @param WP_User $user The user object.
  984. * @return bool[] Filtered array of the user's capabilities.
  985. */
  986. function wp_maybe_grant_site_health_caps( $allcaps, $caps, $args, $user ) {
  987. if ( ! empty( $allcaps['install_plugins'] ) && ( ! is_multisite() || is_super_admin( $user->ID ) ) ) {
  988. $allcaps['view_site_health_checks'] = true;
  989. }
  990. return $allcaps;
  991. }
  992. return;
  993. // Dummy gettext calls to get strings in the catalog.
  994. /* translators: User role for administrators. */
  995. _x( 'Administrator', 'User role' );
  996. /* translators: User role for editors. */
  997. _x( 'Editor', 'User role' );
  998. /* translators: User role for authors. */
  999. _x( 'Author', 'User role' );
  1000. /* translators: User role for contributors. */
  1001. _x( 'Contributor', 'User role' );
  1002. /* translators: User role for subscribers. */
  1003. _x( 'Subscriber', 'User role' );