>
+          },
44
+          "configurations": {
45
+            "production": {
46
+              "fileReplacements": [
47
+                {
48
+                  "replace": "src/environments/environment.ts",
49
+                  "with": "src/environments/environment.prod.ts"
50
+                }
51
+              ],
52
+              "optimization": true,
53
+              "outputHashing": "all",
54
+              "sourceMap": false,
55
+              "namedChunks": false,
56
+              "aot": true,
57
+              "extractLicenses": true,
58
+              "vendorChunk": false,
59
+              "buildOptimizer": true,
60
+              "budgets": [
61
+                {
62
+                  "type": "initial",
63
+                  "maximumWarning": "2mb",
64
+                  "maximumError": "5mb"
65
+                }
66
+              ]
67
+            },
68
+            "ci": {
69
+              "progress": false
70
+            }
71
+          }
72
+        },
73
+        "serve": {
74
+          "builder": "@angular-devkit/build-angular:dev-server",
75
+          "options": {
76
+            "browserTarget": "app:build"
77
+          },
78
+          "configurations": {
79
+            "production": {
80
+              "browserTarget": "app:build:production"
81
+            },
82
+            "ci": {
83
+              "progress": false
84
+            }
85
+          }
86
+        },
87
+        "extract-i18n": {
88
+          "builder": "@angular-devkit/build-angular:extract-i18n",
89
+          "options": {
90
+            "browserTarget": "app:build"
91
+          }
92
+        },
93
+        "test": {
94
+          "builder": "@angular-devkit/build-angular:karma",
95
+          "options": {
96
+            "main": "src/test.ts",
97
+            "polyfills": "src/polyfills.ts",
98
+            "tsConfig": "tsconfig.spec.json",
99
+            "karmaConfig": "karma.conf.js",
100
+            "styles": [],
101
+            "scripts": [],
102
+            "assets": [
103
+              {
104
+                "glob": "favicon.ico",
105
+                "input": "src/",
106
+                "output": "/"
107
+              },
108
+              {
109
+                "glob": "**/*",
110
+                "input": "src/assets",
111
+                "output": "/assets"
112
+              }
113
+            ]
114
+          },
115
+          "configurations": {
116
+            "ci": {
117
+              "progress": false,
118
+              "watch": false
119
+            }
120
+          }
121
+        },
122
+        "lint": {
123
+          "builder": "@angular-eslint/builder:lint",
124
+          "options": {
125
+            "lintFilePatterns": [
126
+              "src/**/*.ts",
127
+              "src/**/*.html"
128
+            ]
129
+          }
130
+        },
131
+        "e2e": {
132
+          "builder": "@angular-devkit/build-angular:protractor",
133
+          "options": {
134
+            "protractorConfig": "e2e/protractor.conf.js",
135
+            "devServerTarget": "app:serve"
136
+          },
137
+          "configurations": {
138
+            "production": {
139
+              "devServerTarget": "app:serve:production"
140
+            },
141
+            "ci": {
142
+              "devServerTarget": "app:serve:ci"
143
+            }
144
+          }
145
+        },
146
+        "ionic-cordova-build": {
147
+          "builder": "@ionic/angular-toolkit:cordova-build",
148
+          "options": {
149
+            "browserTarget": "app:build"
150
+          },
151
+          "configurations": {
152
+            "production": {
153
+              "browserTarget": "app:build:production"
154
+            }
155
+          }
156
+        },
157
+        "ionic-cordova-serve": {
158
+          "builder": "@ionic/angular-toolkit:cordova-serve",
159
+          "options": {
160
+            "cordovaBuildTarget": "app:ionic-cordova-build",
161
+            "devServerTarget": "app:serve"
162
+          },
163
+          "configurations": {
164
+            "production": {
165
+              "cordovaBuildTarget": "app:ionic-cordova-build:production",
166
+              "devServerTarget": "app:serve:production"
167
+            }
168
+          }
169
+        }
170
+      }
171
+    }
172
+  },
173
+  "cli": {
174
+    "analytics": false,
175
+    "defaultCollection": "@ionic/angular-toolkit"
176
+  },
177
+  "schematics": {
178
+    "@ionic/angular-toolkit:component": {
179
+      "styleext": "scss"
180
+    },
181
+    "@ionic/angular-toolkit:page": {
182
+      "styleext": "scss"
183
+    }
184
+  }
185
+}

+ 107 - 0
config.xml

@@ -0,0 +1,107 @@
1
+<?xml version='1.0' encoding='utf-8'?>
2
+<widget id="net.simplico.farmster" version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
3
+    <name>FarmSter</name>
4
+    <description>We are the Best Friend of All Farmers</description>
5
+    <author email="patumos@gmail.com" href="https://www.simplico.net">Simplico Team</author>
6
+    <content src="index.html" />
7
+    <access origin="*" />
8
+    <allow-intent href="http://*/*" />
9
+    <allow-intent href="https://*/*" />
10
+    <allow-intent href="tel:*" />
11
+    <allow-intent href="sms:*" />
12
+    <allow-intent href="mailto:*" />
13
+    <allow-intent href="geo:*" />
14
+    <preference name="ScrollEnabled" value="false" />
15
+    <preference name="BackupWebStorage" value="none" />
16
+    <preference name="SplashMaintainAspectRatio" value="true" />
17
+    <preference name="FadeSplashScreenDuration" value="300" />
18
+    <preference name="SplashShowOnlyFirstTime" value="false" />
19
+    <preference name="SplashScreen" value="screen" />
20
+    <preference name="SplashScreenDelay" value="3000" />
21
+    <preference name="android-targetSdkVersion" value="30" />
22
+    <preference name="android-minSdkVersion" value="16" />
23
+    <platform name="android">
24
+        <edit-config file="app/src/main/AndroidManifest.xml" mode="merge" target="/manifest/application" xmlns:android="http://schemas.android.com/apk/res/android">
25
+            <application android:networkSecurityConfig="@xml/network_security_config" />
26
+        </edit-config>
27
+        <resource-file src="resources/android/xml/network_security_config.xml" target="app/src/main/res/xml/network_security_config.xml" />
28
+        <allow-intent href="market:*" />
29
+        <icon density="ldpi" src="resources/android/icon/drawable-ldpi-icon.png" />
30
+        <icon density="mdpi" src="resources/android/icon/drawable-mdpi-icon.png" />
31
+        <icon density="hdpi" src="resources/android/icon/drawable-hdpi-icon.png" />
32
+        <icon density="xhdpi" src="resources/android/icon/drawable-xhdpi-icon.png" />
33
+        <icon density="xxhdpi" src="resources/android/icon/drawable-xxhdpi-icon.png" />
34
+        <icon density="xxxhdpi" src="resources/android/icon/drawable-xxxhdpi-icon.png" />
35
+        <splash density="land-ldpi" src="resources/android/splash/drawable-land-ldpi-screen.png" />
36
+        <splash density="land-mdpi" src="resources/android/splash/drawable-land-mdpi-screen.png" />
37
+        <splash density="land-hdpi" src="resources/android/splash/drawable-land-hdpi-screen.png" />
38
+        <splash density="land-xhdpi" src="resources/android/splash/drawable-land-xhdpi-screen.png" />
39
+        <splash density="land-xxhdpi" src="resources/android/splash/drawable-land-xxhdpi-screen.png" />
40
+        <splash density="land-xxxhdpi" src="resources/android/splash/drawable-land-xxxhdpi-screen.png" />
41
+        <splash density="port-ldpi" src="resources/android/splash/drawable-port-ldpi-screen.png" />
42
+        <splash density="port-mdpi" src="resources/android/splash/drawable-port-mdpi-screen.png" />
43
+        <splash density="port-hdpi" src="resources/android/splash/drawable-port-hdpi-screen.png" />
44
+        <splash density="port-xhdpi" src="resources/android/splash/drawable-port-xhdpi-screen.png" />
45
+        <splash density="port-xxhdpi" src="resources/android/splash/drawable-port-xxhdpi-screen.png" />
46
+        <splash density="port-xxxhdpi" src="resources/android/splash/drawable-port-xxxhdpi-screen.png" />
47
+    </platform>
48
+    <platform name="ios">
49
+        <allow-intent href="itms:*" />
50
+        <allow-intent href="itms-apps:*" />
51
+        <allow-intent href="tel:*" />
52
+        <edit-config file="*-Info.plist" mode="merge" target="NSLocationWhenInUseUsageDescription">
53
+            <string>Use current location to find nearby businesses</string>
54
+        </edit-config>
55
+        <icon height="57" src="resources/ios/icon/icon.png" width="57" />
56
+        <icon height="114" src="resources/ios/icon/icon@2x.png" width="114" />
57
+        <icon height="20" src="resources/ios/icon/icon-20.png" width="20" />
58
+        <icon height="40" src="resources/ios/icon/icon-20@2x.png" width="40" />
59
+        <icon height="60" src="resources/ios/icon/icon-20@3x.png" width="60" />
60
+        <icon height="48" src="resources/ios/icon/icon-24@2x.png" width="48" />
61
+        <icon height="55" src="resources/ios/icon/icon-27.5@2x.png" width="55" />
62
+        <icon height="29" src="resources/ios/icon/icon-29.png" width="29" />
63
+        <icon height="58" src="resources/ios/icon/icon-29@2x.png" width="58" />
64
+        <icon height="87" src="resources/ios/icon/icon-29@3x.png" width="87" />
65
+        <icon height="40" src="resources/ios/icon/icon-40.png" width="40" />
66
+        <icon height="80" src="resources/ios/icon/icon-40@2x.png" width="80" />
67
+        <icon height="120" src="resources/ios/icon/icon-40@3x.png" width="120" />
68
+        <icon height="88" src="resources/ios/icon/icon-44@2x.png" width="88" />
69
+        <icon height="50" src="resources/ios/icon/icon-50.png" width="50" />
70
+        <icon height="100" src="resources/ios/icon/icon-50@2x.png" width="100" />
71
+        <icon height="60" src="resources/ios/icon/icon-60.png" width="60" />
72
+        <icon height="120" src="resources/ios/icon/icon-60@2x.png" width="120" />
73
+        <icon height="180" src="resources/ios/icon/icon-60@3x.png" width="180" />
74
+        <icon height="72" src="resources/ios/icon/icon-72.png" width="72" />
75
+        <icon height="144" src="resources/ios/icon/icon-72@2x.png" width="144" />
76
+        <icon height="76" src="resources/ios/icon/icon-76.png" width="76" />
77
+        <icon height="152" src="resources/ios/icon/icon-76@2x.png" width="152" />
78
+        <icon height="167" src="resources/ios/icon/icon-83.5@2x.png" width="167" />
79
+        <icon height="172" src="resources/ios/icon/icon-86@2x.png" width="172" />
80
+        <icon height="196" src="resources/ios/icon/icon-98@2x.png" width="196" />
81
+        <icon height="1024" src="resources/ios/icon/icon-1024.png" width="1024" />
82
+        <splash height="480" src="resources/ios/splash/Default~iphone.png" width="320" />
83
+        <splash height="960" src="resources/ios/splash/Default@2x~iphone.png" width="640" />
84
+        <splash height="1024" src="resources/ios/splash/Default-Portrait~ipad.png" width="768" />
85
+        <splash height="768" src="resources/ios/splash/Default-Landscape~ipad.png" width="1024" />
86
+        <splash height="1125" src="resources/ios/splash/Default-Landscape-2436h.png" width="2436" />
87
+        <splash height="1242" src="resources/ios/splash/Default-Landscape-736h.png" width="2208" />
88
+        <splash height="2048" src="resources/ios/splash/Default-Portrait@2x~ipad.png" width="1536" />
89
+        <splash height="1536" src="resources/ios/splash/Default-Landscape@2x~ipad.png" width="2048" />
90
+        <splash height="2732" src="resources/ios/splash/Default-Portrait@~ipadpro.png" width="2048" />
91
+        <splash height="2048" src="resources/ios/splash/Default-Landscape@~ipadpro.png" width="2732" />
92
+        <splash height="1136" src="resources/ios/splash/Default-568h@2x~iphone.png" width="640" />
93
+        <splash height="1334" src="resources/ios/splash/Default-667h.png" width="750" />
94
+        <splash height="2208" src="resources/ios/splash/Default-736h.png" width="1242" />
95
+        <splash height="2436" src="resources/ios/splash/Default-2436h.png" width="1125" />
96
+        <splash height="2732" src="resources/ios/splash/Default@2x~universal~anyany.png" width="2732" />
97
+        <icon height="216" src="resources/ios/icon/icon-108@2x.png" width="216" />
98
+        <splash height="2688" src="resources/ios/splash/Default-2688h~iphone.png" width="1242" />
99
+        <splash height="1242" src="resources/ios/splash/Default-Landscape-2688h~iphone.png" width="2688" />
100
+        <splash height="1792" src="resources/ios/splash/Default-1792h~iphone.png" width="828" />
101
+        <splash height="828" src="resources/ios/splash/Default-Landscape-1792h~iphone.png" width="1792" />
102
+    </platform>
103
+    <plugin name="cordova-plugin-ionic-keyboard" spec="^2.0.5" />
104
+    <allow-navigation href="http://172.16.112.36:8102" sessionid="56964c11" />
105
+    <allow-navigation href="http://localhost:8101" sessionid="9c146598" />
106
+    <allow-navigation href="http://localhost:8100" sessionid="39e2137c" />
107
+</widget>

+ 37 - 0
e2e/protractor.conf.js

@@ -0,0 +1,37 @@
1
+// @ts-check
2
+// Protractor configuration file, see link for more information
3
+// https://github.com/angular/protractor/blob/master/lib/config.ts
4
+
5
+const { SpecReporter, StacktraceOption } = require('jasmine-spec-reporter');
6
+
7
+/**
8
+ * @type { import("protractor").Config }
9
+ */
10
+exports.config = {
11
+  allScriptsTimeout: 11000,
12
+  specs: [
13
+    './src/**/*.e2e-spec.ts'
14
+  ],
15
+  capabilities: {
16
+    browserName: 'chrome'
17
+  },
18
+  directConnect: true,
19
+  SELENIUM_PROMISE_MANAGER: false,
20
+  baseUrl: 'http://localhost:4200/',
21
+  framework: 'jasmine',
22
+  jasmineNodeOpts: {
23
+    showColors: true,
24
+    defaultTimeoutInterval: 30000,
25
+    print: function() {}
26
+  },
27
+  onPrepare() {
28
+    require('ts-node').register({
29
+      project: require('path').join(__dirname, './tsconfig.json')
30
+    });
31
+    jasmine.getEnv().addReporter(new SpecReporter({
32
+      spec: {
33
+        displayStacktrace: StacktraceOption.PRETTY
34
+      }
35
+    }));
36
+  }
37
+};

+ 14 - 0
e2e/src/app.e2e-spec.ts

@@ -0,0 +1,14 @@
1
+import { AppPage } from './app.po';
2
+
3
+describe('new App', () => {
4
+  let page: AppPage;
5
+
6
+  beforeEach(() => {
7
+    page = new AppPage();
8
+  });
9
+
10
+  it('should display welcome message', () => {
11
+    page.navigateTo();
12
+    expect(page.getPageTitle()).toContain('Tab 1');
13
+  });
14
+});

+ 11 - 0
e2e/src/app.po.ts

@@ -0,0 +1,11 @@
1
+import { browser, by, element } from 'protractor';
2
+
3
+export class AppPage {
4
+  navigateTo() {
5
+    return browser.get('/');
6
+  }
7
+
8
+  getPageTitle() {
9
+    return element(by.css('ion-title')).getText();
10
+  }
11
+}

+ 12 - 0
e2e/tsconfig.json

@@ -0,0 +1,12 @@
1
+{
2
+  "extends": "../tsconfig.json",
3
+  "compilerOptions": {
4
+    "outDir": "../out-tsc/e2e",
5
+    "module": "commonjs",
6
+    "target": "es2018",
7
+    "types": [
8
+      "jasmine",
9
+      "node"
10
+    ]
11
+  }
12
+}

+ 39 - 0
google-services.json

@@ -0,0 +1,39 @@
1
+{
2
+  "project_info": {
3
+    "project_number": "1031089119738",
4
+    "project_id": "farmster-c87c8",
5
+    "storage_bucket": "farmster-c87c8.appspot.com"
6
+  },
7
+  "client": [
8
+    {
9
+      "client_info": {
10
+        "mobilesdk_app_id": "1:1031089119738:android:cf99beb19818a7e4d0c443",
11
+        "android_client_info": {
12
+          "package_name": "net.simplico.farmster"
13
+        }
14
+      },
15
+      "oauth_client": [
16
+        {
17
+          "client_id": "1031089119738-pc9qukqqd30aebdh33q51asvbvn6ndhf.apps.googleusercontent.com",
18
+          "client_type": 3
19
+        }
20
+      ],
21
+      "api_key": [
22
+        {
23
+          "current_key": "AIzaSyCviHmTUdl_TL_pR8catbY-gyibTnooUv0"
24
+        }
25
+      ],
26
+      "services": {
27
+        "appinvite_service": {
28
+          "other_platform_oauth_client": [
29
+            {
30
+              "client_id": "1031089119738-pc9qukqqd30aebdh33q51asvbvn6ndhf.apps.googleusercontent.com",
31
+              "client_type": 3
32
+            }
33
+          ]
34
+        }
35
+      }
36
+    }
37
+  ],
38
+  "configuration_version": "1"
39
+}

+ 7 - 0
ionic.config.json

@@ -0,0 +1,7 @@
1
+{
2
+  "name": "hp",
3
+  "integrations": {
4
+    "cordova": {}
5
+  },
6
+  "type": "angular"
7
+}

+ 44 - 0
karma.conf.js

@@ -0,0 +1,44 @@
1
+// Karma configuration file, see link for more information
2
+// https://karma-runner.github.io/1.0/config/configuration-file.html
3
+
4
+module.exports = function (config) {
5
+  config.set({
6
+    basePath: '',
7
+    frameworks: ['jasmine', '@angular-devkit/build-angular'],
8
+    plugins: [
9
+      require('karma-jasmine'),
10
+      require('karma-chrome-launcher'),
11
+      require('karma-jasmine-html-reporter'),
12
+      require('karma-coverage'),
13
+      require('@angular-devkit/build-angular/plugins/karma')
14
+    ],
15
+    client: {
16
+      jasmine: {
17
+        // you can add configuration options for Jasmine here
18
+        // the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
19
+        // for example, you can disable the random execution with `random: false`
20
+        // or set a specific seed with `seed: 4321`
21
+      },
22
+      clearContext: false // leave Jasmine Spec Runner output visible in browser
23
+    },
24
+    jasmineHtmlReporter: {
25
+      suppressAll: true // removes the duplicated traces
26
+    },
27
+    coverageReporter: {
28
+      dir: require('path').join(__dirname, './coverage/ngv'),
29
+      subdir: '.',
30
+      reporters: [
31
+        { type: 'html' },
32
+        { type: 'text-summary' }
33
+      ]
34
+    },
35
+    reporters: ['progress', 'kjhtml'],
36
+    port: 9876,
37
+    colors: true,
38
+    logLevel: config.LOG_INFO,
39
+    autoWatch: true,
40
+    browsers: ['Chrome'],
41
+    singleRun: false,
42
+    restartOnFileChange: true
43
+  });
44
+};

BIN
my-release-key.keystore


Dosya farkı çok büyük olduğundan ihmal edildi
+ 18344 - 0
package-lock.json


+ 180 - 0
package.json

@@ -0,0 +1,180 @@
1
+{
2
+  "name": "hp",
3
+  "version": "0.0.1",
4
+  "author": "Ionic Framework",
5
+  "homepage": "https://ionicframework.com/",
6
+  "scripts": {
7
+    "ng": "ng",
8
+    "start": "ng serve",
9
+    "build": "ng build",
10
+    "test": "ng test",
11
+    "lint": "ng lint",
12
+    "e2e": "ng e2e"
13
+  },
14
+  "private": true,
15
+  "dependencies": {
16
+    "@angular/common": "~13.0.3",
17
+    "@angular/compiler": "~13.0.3",
18
+    "@angular/core": "~13.0.3",
19
+    "@angular/forms": "~13.0.3",
20
+    "@angular/platform-browser": "~13.0.3",
21
+    "@angular/platform-browser-dynamic": "~13.0.3",
22
+    "@angular/router": "~13.0.3",
23
+    "@awesome-cordova-plugins/barcode-scanner": "^5.37.1",
24
+    "@awesome-cordova-plugins/core": "^5.37.1",
25
+    "@awesome-cordova-plugins/file-transfer": "^5.37.1",
26
+    "@awesome-cordova-plugins/push": "^5.37.1",
27
+    "@awesome-cordova-plugins/status-bar": "^5.37.2",
28
+    "@awesome-cordova-plugins/stripe": "^5.37.1",
29
+    "@capacitor/app": "1.0.6",
30
+    "@capacitor/core": "3.3.2",
31
+    "@capacitor/haptics": "1.1.3",
32
+    "@capacitor/ios": "^3.3.2",
33
+    "@capacitor/keyboard": "1.1.3",
34
+    "@capacitor/status-bar": "1.0.6",
35
+    "@fortawesome/angular-fontawesome": "^0.10.1",
36
+    "@fortawesome/fontawesome-free": "^5.15.4",
37
+    "@fortawesome/fontawesome-svg-core": "^1.2.36",
38
+    "@fortawesome/free-brands-svg-icons": "^5.15.4",
39
+    "@fortawesome/free-regular-svg-icons": "^5.15.4",
40
+    "@fortawesome/free-solid-svg-icons": "^5.15.4",
41
+    "@ionic-native/android-permissions": "^5.30.0",
42
+    "@ionic-native/call-number": "^5.36.0",
43
+    "@ionic-native/core": "^5.36.0",
44
+    "@ionic-native/device": "^5.30.0",
45
+    "@ionic-native/email-composer": "^5.36.0",
46
+    "@ionic-native/facebook": "^5.36.0",
47
+    "@ionic-native/geolocation": "^5.36.0",
48
+    "@ionic-native/http": "^5.36.0",
49
+    "@ionic-native/in-app-browser": "^5.36.0",
50
+    "@ionic-native/native-geocoder": "^5.36.0",
51
+    "@ionic-native/paypal": "^5.30.0",
52
+    "@ionic-native/push": "^5.36.0",
53
+    "@ionic-native/sign-in-with-apple": "^5.36.0",
54
+    "@ionic-native/uid": "^5.30.0",
55
+    "@ionic/angular": "^5.9.1",
56
+    "@ionic/storage": "^3.0.6",
57
+    "@ionic/storage-angular": "^3.0.6",
58
+    "abbrev": "^1.1.1",
59
+    "cordova-plugin-device": "2.0.3",
60
+    "cordova-plugin-ionic-webview": "5.0.0",
61
+    "cordova-plugin-splashscreen": "6.0.0",
62
+    "cordova-plugin-whitelist": "1.3.5",
63
+    "ionic-img-viewer": "^2.9.0",
64
+    "lru-cache": "^6.0.0",
65
+    "mimic-fn": "^4.0.0",
66
+    "onetime": "^6.0.0",
67
+    "osenv": "^0.1.5",
68
+    "resolve-url": "^0.2.1",
69
+    "rxjs": "~7.4.0",
70
+    "sharp": "^0.29.3",
71
+    "string.prototype.codepointat": "^1.0.0",
72
+    "tslib": "^2.3.1",
73
+    "yallist": "^4.0.0",
74
+    "zone.js": "~0.11.4"
75
+  },
76
+  "devDependencies": {
77
+    "@angular-devkit/build-angular": "~13.0.4",
78
+    "@angular-eslint/builder": "~13.0.1",
79
+    "@angular-eslint/eslint-plugin": "~13.0.1",
80
+    "@angular-eslint/eslint-plugin-template": "~13.0.1",
81
+    "@angular-eslint/template-parser": "~13.0.1",
82
+    "@angular/cli": "^13.0.4",
83
+    "@angular/compiler": "~13.0.3",
84
+    "@angular/compiler-cli": "~13.0.3",
85
+    "@angular/language-service": "~13.0.3",
86
+    "@capacitor/cli": "3.3.2",
87
+    "@ionic/angular-toolkit": "^5.0.3",
88
+    "@types/jasmine": "~3.10.2",
89
+    "@types/jasminewd2": "~2.0.10",
90
+    "@types/node": "^16.11.11",
91
+    "@typescript-eslint/eslint-plugin": "4.16.1",
92
+    "@typescript-eslint/parser": "4.16.1",
93
+    "call-number": "^1.0.1",
94
+    "card.io.cordova.mobilesdk": "^2.1.0",
95
+    "com.paypal.cordova.mobilesdk": "^3.5.0",
96
+    "cordova-android": "^9.1.0",
97
+    "cordova-ios": "^6.2.0",
98
+    "cordova-plugin-add-swift-support": "^2.0.2",
99
+    "cordova-plugin-advanced-http": "^3.2.2",
100
+    "cordova-plugin-android-permissions": "^1.1.2",
101
+    "cordova-plugin-androidx": "^3.0.0",
102
+    "cordova-plugin-androidx-adapter": "^1.1.3",
103
+    "cordova-plugin-email-composer": "^0.10.0",
104
+    "cordova-plugin-facebook-connect": "^3.2.0",
105
+    "cordova-plugin-file": "^6.0.2",
106
+    "cordova-plugin-file-transfer": "^1.7.1",
107
+    "cordova-plugin-geolocation": "^4.1.0",
108
+    "cordova-plugin-googlemaps": "^2.7.1",
109
+    "cordova-plugin-googlemaps-sdk": "github:mapsplugin/cordova-plugin-googlemaps-sdk",
110
+    "cordova-plugin-inappbrowser": "^5.0.0",
111
+    "cordova-plugin-ionic-keyboard": "^2.2.0",
112
+    "cordova-plugin-nativegeocoder": "^3.4.1",
113
+    "cordova-plugin-sign-in-with-apple": "^0.1.2",
114
+    "cordova-plugin-stripe": "^1.5.3",
115
+    "cordova-plugin-uid": "^1.3.0",
116
+    "cordova-support-google-services": "^1.4.1",
117
+    "eslint": "^8.3.0",
118
+    "eslint-plugin-import": "2.25.3",
119
+    "eslint-plugin-jsdoc": "37.1.0",
120
+    "eslint-plugin-prefer-arrow": "1.2.3",
121
+    "jasmine-core": "~3.10.1",
122
+    "jasmine-spec-reporter": "~7.0.0",
123
+    "karma": "~6.3.9",
124
+    "karma-chrome-launcher": "~3.1.0",
125
+    "karma-coverage": "~2.1.0",
126
+    "karma-coverage-istanbul-reporter": "~3.0.3",
127
+    "karma-jasmine": "~4.0.1",
128
+    "karma-jasmine-html-reporter": "^1.7.0",
129
+    "mx.ferreyra.callnumber": "0.0.2",
130
+    "phonegap-plugin-multidex": "^1.0.0",
131
+    "phonegap-plugin-push": "^2.3.0",
132
+    "protractor": "~7.0.0",
133
+    "ts-node": "~10.4.0",
134
+    "typescript": "~4.2.4"
135
+  },
136
+  "description": "An Ionic project",
137
+  "cordova": {
138
+    "plugins": {
139
+      "cordova-plugin-ionic-keyboard": {},
140
+      "cordova-plugin-geolocation": {},
141
+      "cordova-plugin-nativegeocoder": {
142
+        "LOCATION_WHEN_IN_USE_DESCRIPTION": "Use geocoder service"
143
+      },
144
+      "mx.ferreyra.callnumber": {},
145
+      "cordova-plugin-email-composer": {},
146
+      "call-number": {},
147
+      "cordova-plugin-inappbrowser": {},
148
+      "cordova-plugin-sign-in-with-apple": {},
149
+      "cordova-plugin-androidx": {},
150
+      "cordova-plugin-androidx-adapter": {},
151
+      "phonegap-plugin-push": {},
152
+      "cordova-plugin-file-transfer": {},
153
+      "cordova-plugin-stripe": {},
154
+      "com.paypal.cordova.mobilesdk": {},
155
+      "cordova-plugin-whitelist": {},
156
+      "cordova-plugin-device": {},
157
+      "cordova-plugin-splashscreen": {},
158
+      "cordova-plugin-ionic-webview": {},
159
+      "cordova-plugin-facebook-connect": {
160
+        "APP_ID": "4746807965342529",
161
+        "APP_NAME": "FarmSter",
162
+        "FACEBOOK_URL_SCHEME_SUFFIX": " ",
163
+        "OTHER_APP_SCHEMES": " ",
164
+        "FACEBOOK_AUTO_LOG_APP_EVENTS": "true",
165
+        "FACEBOOK_HYBRID_APP_EVENTS": "false",
166
+        "FACEBOOK_ADVERTISER_ID_COLLECTION": "true",
167
+        "FACEBOOK_ANDROID_SDK_VERSION": "11.3.0",
168
+        "FACEBOOK_IOS_SDK_VERSION": "11.1.0",
169
+        "FACEBOOK_BROWSER_SDK_VERSION": "v11.0"
170
+      },
171
+      "cordova-plugin-statusbar": {},
172
+      "cordova-plugin-android-permissions": {},
173
+      "cordova-plugin-dreamover-uid": {}
174
+    },
175
+    "platforms": [
176
+      "ios",
177
+      "android"
178
+    ]
179
+  }
180
+}

+ 8 - 0
resources/README.md

@@ -0,0 +1,8 @@
1
+These are Cordova resources. You can replace icon.png and splash.png and run
2
+`ionic cordova resources` to generate custom icons and splash screens for your
3
+app. See `ionic cordova resources --help` for details.
4
+
5
+Cordova reference documentation:
6
+
7
+- Icons: https://cordova.apache.org/docs/en/latest/config_ref/images.html
8
+- Splash Screens: https://cordova.apache.org/docs/en/latest/reference/cordova-plugin-splashscreen/

BIN
resources/android/icon/drawable-hdpi-icon.png


BIN
resources/android/icon/drawable-ldpi-icon.png


BIN
resources/android/icon/drawable-mdpi-icon.png


BIN
resources/android/icon/drawable-xhdpi-icon.png


BIN
resources/android/icon/drawable-xxhdpi-icon.png


BIN
resources/android/icon/drawable-xxxhdpi-icon.png


BIN
resources/android/splash/drawable-land-hdpi-screen.png


BIN
resources/android/splash/drawable-land-ldpi-screen.png


BIN
resources/android/splash/drawable-land-mdpi-screen.png


BIN
resources/android/splash/drawable-land-xhdpi-screen.png


BIN
resources/android/splash/drawable-land-xxhdpi-screen.png


BIN
resources/android/splash/drawable-land-xxxhdpi-screen.png


BIN
resources/android/splash/drawable-port-hdpi-screen.png


BIN
resources/android/splash/drawable-port-ldpi-screen.png


BIN
resources/android/splash/drawable-port-mdpi-screen.png


BIN
resources/android/splash/drawable-port-xhdpi-screen.png


BIN
resources/android/splash/drawable-port-xxhdpi-screen.png


BIN
resources/android/splash/drawable-port-xxxhdpi-screen.png


+ 7 - 0
resources/android/xml/network_security_config.xml

@@ -0,0 +1,7 @@
1
+<?xml version="1.0" encoding="utf-8"?>
2
+<network-security-config>
3
+    <domain-config cleartextTrafficPermitted="true">
4
+        <domain includeSubdomains="true">localhost</domain>
5
+        <domain includeSubdomains="true">tamtime.iamarray.xyz</domain>
6
+    </domain-config>
7
+</network-security-config>

BIN
resources/icon.png


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


BIN
resources/ios/icon/icon.png


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


BIN
resources/ios/splash/Default-1792h~iphone.png


BIN
resources/ios/splash/Default-2436h.png


BIN
resources/ios/splash/Default-2688h~iphone.png


BIN
resources/ios/splash/Default-568h@2x~iphone.png


BIN
resources/ios/splash/Default-667h.png


BIN
resources/ios/splash/Default-736h.png


BIN
resources/ios/splash/Default-Landscape-1792h~iphone.png


BIN
resources/ios/splash/Default-Landscape-2436h.png


BIN
resources/ios/splash/Default-Landscape-2688h~iphone.png


BIN
resources/ios/splash/Default-Landscape-736h.png


BIN
resources/ios/splash/Default-Landscape@2x~ipad.png


BIN
resources/ios/splash/Default-Landscape@~ipadpro.png


BIN
resources/ios/splash/Default-Landscape~ipad.png


BIN
resources/ios/splash/Default-Portrait@2x~ipad.png


BIN
resources/ios/splash/Default-Portrait@~ipadpro.png


BIN
resources/ios/splash/Default-Portrait~ipad.png


BIN
resources/ios/splash/Default@2x~iphone.png


BIN
resources/ios/splash/Default@2x~universal~anyany.png


BIN
resources/ios/splash/Default~iphone.png


BIN
resources/splash.png


+ 92 - 0
src/app/app-routing.module.ts

@@ -0,0 +1,92 @@
1
+import { NgModule } from '@angular/core';
2
+import { PreloadAllModules, RouterModule, Routes } from '@angular/router';
3
+
4
+const routes: Routes = [
5
+  {
6
+    path: '',
7
+    loadChildren: () => import('./tabs/tabs.module').then(m => m.TabsPageModule)
8
+  },
9
+  {
10
+    path: 'home',
11
+    loadChildren: () => import('./home/home.module').then( m => m.HomePageModule)
12
+  },
13
+  {
14
+    path: 'register',
15
+    loadChildren: () => import('./register/register.module').then( m => m.RegisterPageModule)
16
+  },
17
+  {
18
+    path: 'login',
19
+    loadChildren: () => import('./login/login.module').then( m => m.LoginPageModule)
20
+  },
21
+  {
22
+    path: 'forgot-password',
23
+    loadChildren: () => import('./forgot-password/forgot-password.module').then( m => m.ForgotPasswordPageModule)
24
+  },
25
+  {
26
+    path: 'register-form',
27
+    loadChildren: () => import('./register-form/register-form.module').then( m => m.RegisterFormPageModule)
28
+  },
29
+  {
30
+    path: 'profile',
31
+    loadChildren: () => import('./profile/profile.module').then( m => m.ProfilePageModule)
32
+  },
33
+  {
34
+    path: 'reset-password',
35
+    loadChildren: () => import('./reset-password/reset-password.module').then( m => m.ResetPasswordPageModule)
36
+  },
37
+  {
38
+    path: 'placedetail',
39
+    loadChildren: () => import('./placedetail/placedetail.module').then( m => m.PlacedetailPageModule)
40
+  },
41
+  {
42
+    path: 'place',
43
+    loadChildren: () => import('./place/place.module').then( m => m.PlacePageModule)
44
+  },
45
+  {
46
+    path: 'province',
47
+    loadChildren: () => import('./province/province.module').then( m => m.ProvincePageModule)
48
+  },
49
+  {
50
+    path: 'gmap',
51
+    loadChildren: () => import('./gmap/gmap.module').then( m => m.GmapPageModule)
52
+  },
53
+  {
54
+    path: 'nearme',
55
+    loadChildren: () => import('./nearme/nearme.module').then( m => m.NearmePageModule)
56
+  },
57
+  {
58
+    path: 'sample',
59
+    loadChildren: () => import('./sample/sample.module').then( m => m.SamplePageModule)
60
+  },
61
+  {
62
+    path: 'product',
63
+    loadChildren: () => import('./product/product.module').then( m => m.ProductPageModule)
64
+  },
65
+  {
66
+    path: 'product-detail',
67
+    loadChildren: () => import('./product-detail/product-detail.module').then( m => m.ProductDetailPageModule)
68
+  },
69
+  {
70
+    path: 'tracking',
71
+    loadChildren: () => import('./tracking/tracking.module').then( m => m.TrackingPageModule)
72
+  },
73
+  {
74
+    path: 'news',
75
+    loadChildren: () => import('./news/news.module').then( m => m.NewsPageModule)
76
+  },
77
+  {
78
+    path: 'checkout',
79
+    loadChildren: () => import('./checkout/checkout.module').then( m => m.CheckoutPageModule)
80
+  },
81
+  {
82
+    path: 'pay',
83
+    loadChildren: () => import('./pay/pay.module').then( m => m.PayPageModule)
84
+  }
85
+];
86
+@NgModule({
87
+  imports: [
88
+    RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules })
89
+  ],
90
+  exports: [RouterModule]
91
+})
92
+export class AppRoutingModule {}

+ 3 - 0
src/app/app.component.html

@@ -0,0 +1,3 @@
1
+<ion-app>
2
+  <ion-router-outlet></ion-router-outlet>
3
+</ion-app>

+ 4 - 0
src/app/app.component.scss

@@ -0,0 +1,4 @@
1
+.product-title {
2
+    font-weight:bold;
3
+    font-size:20px;
4
+}

+ 23 - 0
src/app/app.component.spec.ts

@@ -0,0 +1,23 @@
1
+import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
2
+import { TestBed, waitForAsync } from '@angular/core/testing';
3
+
4
+import { AppComponent } from './app.component';
5
+
6
+describe('AppComponent', () => {
7
+
8
+  beforeEach(waitForAsync(() => {
9
+
10
+    TestBed.configureTestingModule({
11
+      declarations: [AppComponent],
12
+      schemas: [CUSTOM_ELEMENTS_SCHEMA],
13
+    }).compileComponents();
14
+  }));
15
+
16
+  it('should create the app', () => {
17
+    const fixture = TestBed.createComponent(AppComponent);
18
+    const app = fixture.debugElement.componentInstance;
19
+    expect(app).toBeTruthy();
20
+  });
21
+  // TODO: add more tests!
22
+
23
+});

+ 52 - 0
src/app/app.component.ts

@@ -0,0 +1,52 @@
1
+import { Component } from '@angular/core';
2
+import { Storage } from '@ionic/storage-angular';
3
+import { StatusBar } from '@awesome-cordova-plugins/status-bar/ngx';
4
+import { Uid } from '@ionic-native/uid/ngx';
5
+import { AndroidPermissions } from '@ionic-native/android-permissions/ngx';
6
+
7
+
8
+@Component({
9
+  selector: 'app-root',
10
+  templateUrl: 'app.component.html',
11
+  styleUrls: ['app.component.scss'],
12
+})
13
+export class AppComponent {
14
+    constructor(private storage: Storage, private statusBar:StatusBar, private uid: Uid, private androidPermissions: AndroidPermissions) {
15
+
16
+        this.statusBar.overlaysWebView(true);
17
+
18
+
19
+    }
20
+    openQRCode() {
21
+        console.log("open QR Code");
22
+    }
23
+    async ngOnInit() {
24
+        // If using a custom driver:
25
+        // await this.storage.defineDriver(MyCustomDriver)
26
+        await this.storage.create();
27
+        //await this.getImei();
28
+    }
29
+    async getImei() {
30
+        const { hasPermission } = await this.androidPermissions.checkPermission(
31
+            this.androidPermissions.PERMISSION.READ_PHONE_STATE
32
+        );
33
+        console.log(hasPermission);
34
+
35
+        if (!hasPermission) {
36
+            const result = await this.androidPermissions.requestPermission(
37
+                this.androidPermissions.PERMISSION.READ_PHONE_STATE
38
+            );
39
+
40
+            if (!result.hasPermission) {
41
+                throw new Error('Permissions required');
42
+            }
43
+
44
+            // ok, a user gave us permission, we can get him identifiers after restart app
45
+            return;
46
+        }
47
+        console.log("UUID");
48
+        console.log(this.uid.UUID);
49
+        await this.storage.set('uid', this.uid.UUID);
50
+        return this.uid.IMEI
51
+    }
52
+}

+ 77 - 0
src/app/app.module.ts

@@ -0,0 +1,77 @@
1
+import { NgModule } from '@angular/core';
2
+import { BrowserModule } from '@angular/platform-browser';
3
+import { RouteReuseStrategy } from '@angular/router';
4
+
5
+import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
6
+
7
+import { AppRoutingModule } from './app-routing.module';
8
+import { AppComponent } from './app.component';
9
+
10
+import { HttpClientModule, HttpClient} from '@angular/common/http';
11
+
12
+// import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
13
+// import { library } from '@fortawesome/fontawesome-svg-core';
14
+import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontawesome';
15
+import { fas } from '@fortawesome/free-solid-svg-icons';
16
+import { far } from '@fortawesome/free-regular-svg-icons';
17
+import { fab } from '@fortawesome/free-brands-svg-icons';
18
+
19
+import { Geolocation } from '@ionic-native/geolocation/ngx';
20
+import { NativeGeocoder } from '@ionic-native/native-geocoder/ngx';
21
+
22
+import { CallNumber } from '@ionic-native/call-number/ngx';
23
+import { EmailComposer } from '@ionic-native/email-composer/ngx';
24
+import { HTTP } from '@ionic-native/http/ngx';
25
+import { InAppBrowser } from '@ionic-native/in-app-browser/ngx';
26
+
27
+import { IonicStorageModule } from '@ionic/storage-angular';
28
+import { SignInWithApple } from '@ionic-native/sign-in-with-apple/ngx';
29
+
30
+import { PipeModule } from './pipe/pipe.module';
31
+import { Facebook } from '@ionic-native/facebook/ngx';
32
+
33
+import { BarcodeScanner } from '@awesome-cordova-plugins/barcode-scanner/ngx';
34
+import { PayPal } from '@ionic-native/paypal/ngx';
35
+import { StatusBar } from '@awesome-cordova-plugins/status-bar/ngx';
36
+import { Uid } from '@ionic-native/uid/ngx';
37
+import { AndroidPermissions } from '@ionic-native/android-permissions/ngx';
38
+import { Device } from '@ionic-native/device/ngx';
39
+
40
+//import { Stripe } from '@awesome-cordova-plugins/stripe/ngx';
41
+
42
+
43
+// import { IonicImageViewerModule } from 'ionic-img-viewer';
44
+
45
+
46
+// library.add(fas,far,fab);
47
+
48
+@NgModule({
49
+    declarations: [AppComponent],
50
+    entryComponents: [],
51
+    imports: [HttpClientModule, BrowserModule, PipeModule,IonicModule.forRoot({_forceStatusbarPadding: true}), AppRoutingModule, FontAwesomeModule,   IonicStorageModule.forRoot()],
52
+    providers: [
53
+        Geolocation,
54
+        NativeGeocoder,
55
+        CallNumber,
56
+        EmailComposer,
57
+        InAppBrowser,
58
+        HTTP,
59
+        Facebook, 
60
+        StatusBar,
61
+        BarcodeScanner,
62
+        SignInWithApple,
63
+        PayPal,
64
+        AndroidPermissions,
65
+        Uid,
66
+        Device,
67
+        //Stripe,
68
+        { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }
69
+    ],
70
+    bootstrap: [AppComponent],
71
+})
72
+export class AppModule {
73
+    constructor(library: FaIconLibrary) {
74
+        library.addIconPacks(fas, fab, far);
75
+    }
76
+
77
+}

+ 17 - 0
src/app/checkout/checkout-routing.module.ts

@@ -0,0 +1,17 @@
1
+import { NgModule } from '@angular/core';
2
+import { Routes, RouterModule } from '@angular/router';
3
+
4
+import { CheckoutPage } from './checkout.page';
5
+
6
+const routes: Routes = [
7
+  {
8
+    path: '',
9
+    component: CheckoutPage
10
+  }
11
+];
12
+
13
+@NgModule({
14
+  imports: [RouterModule.forChild(routes)],
15
+  exports: [RouterModule],
16
+})
17
+export class CheckoutPageRoutingModule {}

+ 20 - 0
src/app/checkout/checkout.module.ts

@@ -0,0 +1,20 @@
1
+import { NgModule } from '@angular/core';
2
+import { CommonModule } from '@angular/common';
3
+import { FormsModule } from '@angular/forms';
4
+
5
+import { IonicModule } from '@ionic/angular';
6
+
7
+import { CheckoutPageRoutingModule } from './checkout-routing.module';
8
+
9
+import { CheckoutPage } from './checkout.page';
10
+
11
+@NgModule({
12
+  imports: [
13
+    CommonModule,
14
+    FormsModule,
15
+    IonicModule,
16
+    CheckoutPageRoutingModule
17
+  ],
18
+  declarations: [CheckoutPage]
19
+})
20
+export class CheckoutPageModule {}

+ 0 - 0
src/app/checkout/checkout.page.html


Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor

tum/whitesports - Gogs: Simplico Git Service

Brak opisu

ms-site.php 39KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299
  1. <?php
  2. /**
  3. * Site API
  4. *
  5. * @package WordPress
  6. * @subpackage Multisite
  7. * @since 5.1.0
  8. */
  9. /**
  10. * Inserts a new site into the database.
  11. *
  12. * @since 5.1.0
  13. *
  14. * @global wpdb $wpdb WordPress database abstraction object.
  15. *
  16. * @param array $data {
  17. * Data for the new site that should be inserted.
  18. *
  19. * @type string $domain Site domain. Default empty string.
  20. * @type string $path Site path. Default '/'.
  21. * @type int $network_id The site's network ID. Default is the current network ID.
  22. * @type string $registered When the site was registered, in SQL datetime format. Default is
  23. * the current time.
  24. * @type string $last_updated When the site was last updated, in SQL datetime format. Default is
  25. * the value of $registered.
  26. * @type int $public Whether the site is public. Default 1.
  27. * @type int $archived Whether the site is archived. Default 0.
  28. * @type int $mature Whether the site is mature. Default 0.
  29. * @type int $spam Whether the site is spam. Default 0.
  30. * @type int $deleted Whether the site is deleted. Default 0.
  31. * @type int $lang_id The site's language ID. Currently unused. Default 0.
  32. * @type int $user_id User ID for the site administrator. Passed to the
  33. * `wp_initialize_site` hook.
  34. * @type string $title Site title. Default is 'Site %d' where %d is the site ID. Passed
  35. * to the `wp_initialize_site` hook.
  36. * @type array $options Custom option $key => $value pairs to use. Default empty array. Passed
  37. * to the `wp_initialize_site` hook.
  38. * @type array $meta Custom site metadata $key => $value pairs to use. Default empty array.
  39. * Passed to the `wp_initialize_site` hook.
  40. * }
  41. * @return int|WP_Error The new site's ID on success, or error object on failure.
  42. */
  43. function wp_insert_site( array $data ) {
  44. global $wpdb;
  45. $now = current_time( 'mysql', true );
  46. $defaults = array(
  47. 'domain' => '',
  48. 'path' => '/',
  49. 'network_id' => get_current_network_id(),
  50. 'registered' => $now,
  51. 'last_updated' => $now,
  52. 'public' => 1,
  53. 'archived' => 0,
  54. 'mature' => 0,
  55. 'spam' => 0,
  56. 'deleted' => 0,
  57. 'lang_id' => 0,
  58. );
  59. $prepared_data = wp_prepare_site_data( $data, $defaults );
  60. if ( is_wp_error( $prepared_data ) ) {
  61. return $prepared_data;
  62. }
  63. if ( false === $wpdb->insert( $wpdb->blogs, $prepared_data ) ) {
  64. return new WP_Error( 'db_insert_error', __( 'Could not insert site into the database.' ), $wpdb->last_error );
  65. }
  66. $site_id = (int) $wpdb->insert_id;
  67. clean_blog_cache( $site_id );
  68. $new_site = get_site( $site_id );
  69. if ( ! $new_site ) {
  70. return new WP_Error( 'get_site_error', __( 'Could not retrieve site data.' ) );
  71. }
  72. /**
  73. * Fires once a site has been inserted into the database.
  74. *
  75. * @since 5.1.0
  76. *
  77. * @param WP_Site $new_site New site object.
  78. */
  79. do_action( 'wp_insert_site', $new_site );
  80. // Extract the passed arguments that may be relevant for site initialization.
  81. $args = array_diff_key( $data, $defaults );
  82. if ( isset( $args['site_id'] ) ) {
  83. unset( $args['site_id'] );
  84. }
  85. /**
  86. * Fires when a site's initialization routine should be executed.
  87. *
  88. * @since 5.1.0
  89. *
  90. * @param WP_Site $new_site New site object.
  91. * @param array $args Arguments for the initialization.
  92. */
  93. do_action( 'wp_initialize_site', $new_site, $args );
  94. // Only compute extra hook parameters if the deprecated hook is actually in use.
  95. if ( has_action( 'wpmu_new_blog' ) ) {
  96. $user_id = ! empty( $args['user_id'] ) ? $args['user_id'] : 0;
  97. $meta = ! empty( $args['options'] ) ? $args['options'] : array();
  98. // WPLANG was passed with `$meta` to the `wpmu_new_blog` hook prior to 5.1.0.
  99. if ( ! array_key_exists( 'WPLANG', $meta ) ) {
  100. $meta['WPLANG'] = get_network_option( $new_site->network_id, 'WPLANG' );
  101. }
  102. // Rebuild the data expected by the `wpmu_new_blog` hook prior to 5.1.0 using allowed keys.
  103. // The `$allowed_data_fields` matches the one used in `wpmu_create_blog()`.
  104. $allowed_data_fields = array( 'public', 'archived', 'mature', 'spam', 'deleted', 'lang_id' );
  105. $meta = array_merge( array_intersect_key( $data, array_flip( $allowed_data_fields ) ), $meta );
  106. /**
  107. * Fires immediately after a new site is created.
  108. *
  109. * @since MU (3.0.0)
  110. * @deprecated 5.1.0 Use {@see 'wp_insert_site'} instead.
  111. *
  112. * @param int $site_id Site ID.
  113. * @param int $user_id User ID.
  114. * @param string $domain Site domain.
  115. * @param string $path Site path.
  116. * @param int $network_id Network ID. Only relevant on multi-network installations.
  117. * @param array $meta Meta data. Used to set initial site options.
  118. */
  119. do_action_deprecated(
  120. 'wpmu_new_blog',
  121. array( $new_site->id, $user_id, $new_site->domain, $new_site->path, $new_site->network_id, $meta ),
  122. '5.1.0',
  123. 'wp_insert_site'
  124. );
  125. }
  126. return (int) $new_site->id;
  127. }
  128. /**
  129. * Updates a site in the database.
  130. *
  131. * @since 5.1.0
  132. *
  133. * @global wpdb $wpdb WordPress database abstraction object.
  134. *
  135. * @param int $site_id ID of the site that should be updated.
  136. * @param array $data Site data to update. See {@see wp_insert_site()} for the list of supported keys.
  137. * @return int|WP_Error The updated site's ID on success, or error object on failure.
  138. */
  139. function wp_update_site( $site_id, array $data ) {
  140. global $wpdb;
  141. if ( empty( $site_id ) ) {
  142. return new WP_Error( 'site_empty_id', __( 'Site ID must not be empty.' ) );
  143. }
  144. $old_site = get_site( $site_id );
  145. if ( ! $old_site ) {
  146. return new WP_Error( 'site_not_exist', __( 'Site does not exist.' ) );
  147. }
  148. $defaults = $old_site->to_array();
  149. $defaults['network_id'] = (int) $defaults['site_id'];
  150. $defaults['last_updated'] = current_time( 'mysql', true );
  151. unset( $defaults['blog_id'], $defaults['site_id'] );
  152. $data = wp_prepare_site_data( $data, $defaults, $old_site );
  153. if ( is_wp_error( $data ) ) {
  154. return $data;
  155. }
  156. if ( false === $wpdb->update( $wpdb->blogs, $data, array( 'blog_id' => $old_site->id ) ) ) {
  157. return new WP_Error( 'db_update_error', __( 'Could not update site in the database.' ), $wpdb->last_error );
  158. }
  159. clean_blog_cache( $old_site );
  160. $new_site = get_site( $old_site->id );
  161. /**
  162. * Fires once a site has been updated in the database.
  163. *
  164. * @since 5.1.0
  165. *
  166. * @param WP_Site $new_site New site object.
  167. * @param WP_Site $old_site Old site object.
  168. */
  169. do_action( 'wp_update_site', $new_site, $old_site );
  170. return (int) $new_site->id;
  171. }
  172. /**
  173. * Deletes a site from the database.
  174. *
  175. * @since 5.1.0
  176. *
  177. * @global wpdb $wpdb WordPress database abstraction object.
  178. *
  179. * @param int $site_id ID of the site that should be deleted.
  180. * @return WP_Site|WP_Error The deleted site object on success, or error object on failure.
  181. */
  182. function wp_delete_site( $site_id ) {
  183. global $wpdb;
  184. if ( empty( $site_id ) ) {
  185. return new WP_Error( 'site_empty_id', __( 'Site ID must not be empty.' ) );
  186. }
  187. $old_site = get_site( $site_id );
  188. if ( ! $old_site ) {
  189. return new WP_Error( 'site_not_exist', __( 'Site does not exist.' ) );
  190. }
  191. $errors = new WP_Error();
  192. /**
  193. * Fires before a site should be deleted from the database.
  194. *
  195. * Plugins should amend the `$errors` object via its `WP_Error::add()` method. If any errors
  196. * are present, the site will not be deleted.
  197. *
  198. * @since 5.1.0
  199. *
  200. * @param WP_Error $errors Error object to add validation errors to.
  201. * @param WP_Site $old_site The site object to be deleted.
  202. */
  203. do_action( 'wp_validate_site_deletion', $errors, $old_site );
  204. if ( ! empty( $errors->errors ) ) {
  205. return $errors;
  206. }
  207. /**
  208. * Fires before a site is deleted.
  209. *
  210. * @since MU (3.0.0)
  211. * @deprecated 5.1.0
  212. *
  213. * @param int $site_id The site ID.
  214. * @param bool $drop True if site's table should be dropped. Default false.
  215. */
  216. do_action_deprecated( 'delete_blog', array( $old_site->id, true ), '5.1.0' );
  217. /**
  218. * Fires when a site's uninitialization routine should be executed.
  219. *
  220. * @since 5.1.0
  221. *
  222. * @param WP_Site $old_site Deleted site object.
  223. */
  224. do_action( 'wp_uninitialize_site', $old_site );
  225. if ( is_site_meta_supported() ) {
  226. $blog_meta_ids = $wpdb->get_col( $wpdb->prepare( "SELECT meta_id FROM $wpdb->blogmeta WHERE blog_id = %d ", $old_site->id ) );
  227. foreach ( $blog_meta_ids as $mid ) {
  228. delete_metadata_by_mid( 'blog', $mid );
  229. }
  230. }
  231. if ( false === $wpdb->delete( $wpdb->blogs, array( 'blog_id' => $old_site->id ) ) ) {
  232. return new WP_Error( 'db_delete_error', __( 'Could not delete site from the database.' ), $wpdb->last_error );
  233. }
  234. clean_blog_cache( $old_site );
  235. /**
  236. * Fires once a site has been deleted from the database.
  237. *
  238. * @since 5.1.0
  239. *
  240. * @param WP_Site $old_site Deleted site object.
  241. */
  242. do_action( 'wp_delete_site', $old_site );
  243. /**
  244. * Fires after the site is deleted from the network.
  245. *
  246. * @since 4.8.0
  247. * @deprecated 5.1.0
  248. *
  249. * @param int $site_id The site ID.
  250. * @param bool $drop True if site's tables should be dropped. Default false.
  251. */
  252. do_action_deprecated( 'deleted_blog', array( $old_site->id, true ), '5.1.0' );
  253. return $old_site;
  254. }
  255. /**
  256. * Retrieves site data given a site ID or site object.
  257. *
  258. * Site data will be cached and returned after being passed through a filter.
  259. * If the provided site is empty, the current site global will be used.
  260. *
  261. * @since 4.6.0
  262. *
  263. * @param WP_Site|int|null $site Optional. Site to retrieve. Default is the current site.
  264. * @return WP_Site|null The site object or null if not found.
  265. */
  266. function get_site( $site = null ) {
  267. if ( empty( $site ) ) {
  268. $site = get_current_blog_id();
  269. }
  270. if ( $site instanceof WP_Site ) {
  271. $_site = $site;
  272. } elseif ( is_object( $site ) ) {
  273. $_site = new WP_Site( $site );
  274. } else {
  275. $_site = WP_Site::get_instance( $site );
  276. }
  277. if ( ! $_site ) {
  278. return null;
  279. }
  280. /**
  281. * Fires after a site is retrieved.
  282. *
  283. * @since 4.6.0
  284. *
  285. * @param WP_Site $_site Site data.
  286. */
  287. $_site = apply_filters( 'get_site', $_site );
  288. return $_site;
  289. }
  290. /**
  291. * Adds any sites from the given IDs to the cache that do not already exist in cache.
  292. *
  293. * @since 4.6.0
  294. * @since 5.1.0 Introduced the `$update_meta_cache` parameter.
  295. * @access private
  296. *
  297. * @see update_site_cache()
  298. * @global wpdb $wpdb WordPress database abstraction object.
  299. *
  300. * @param array $ids ID list.
  301. * @param bool $update_meta_cache Optional. Whether to update the meta cache. Default true.
  302. */
  303. function _prime_site_caches( $ids, $update_meta_cache = true ) {
  304. global $wpdb;
  305. $non_cached_ids = _get_non_cached_ids( $ids, 'sites' );
  306. if ( ! empty( $non_cached_ids ) ) {
  307. $fresh_sites = $wpdb->get_results( sprintf( "SELECT * FROM $wpdb->blogs WHERE blog_id IN (%s)", implode( ',', array_map( 'intval', $non_cached_ids ) ) ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
  308. update_site_cache( $fresh_sites, $update_meta_cache );
  309. }
  310. }
  311. /**
  312. * Updates sites in cache.
  313. *
  314. * @since 4.6.0
  315. * @since 5.1.0 Introduced the `$update_meta_cache` parameter.
  316. *
  317. * @param array $sites Array of site objects.
  318. * @param bool $update_meta_cache Whether to update site meta cache. Default true.
  319. */
  320. function update_site_cache( $sites, $update_meta_cache = true ) {
  321. if ( ! $sites ) {
  322. return;
  323. }
  324. $site_ids = array();
  325. foreach ( $sites as $site ) {
  326. $site_ids[] = $site->blog_id;
  327. wp_cache_add( $site->blog_id, $site, 'sites' );
  328. wp_cache_add( $site->blog_id . 'short', $site, 'blog-details' );
  329. }
  330. if ( $update_meta_cache ) {
  331. update_sitemeta_cache( $site_ids );
  332. }
  333. }
  334. /**
  335. * Updates metadata cache for list of site IDs.
  336. *
  337. * Performs SQL query to retrieve all metadata for the sites matching `$site_ids` and stores them in the cache.
  338. * Subsequent calls to `get_site_meta()` will not need to query the database.
  339. *
  340. * @since 5.1.0
  341. *
  342. * @param array $site_ids List of site IDs.
  343. * @return array|false An array of metadata on success, false if there is nothing to update.
  344. */
  345. function update_sitemeta_cache( $site_ids ) {
  346. // Ensure this filter is hooked in even if the function is called early.
  347. if ( ! has_filter( 'update_blog_metadata_cache', 'wp_check_site_meta_support_prefilter' ) ) {
  348. add_filter( 'update_blog_metadata_cache', 'wp_check_site_meta_support_prefilter' );
  349. }
  350. return update_meta_cache( 'blog', $site_ids );
  351. }
  352. /**
  353. * Retrieves a list of sites matching requested arguments.
  354. *
  355. * @since 4.6.0
  356. * @since 4.8.0 Introduced the 'lang_id', 'lang__in', and 'lang__not_in' parameters.
  357. *
  358. * @see WP_Site_Query::parse_query()
  359. *
  360. * @param string|array $args Optional. Array or string of arguments. See WP_Site_Query::__construct()
  361. * for information on accepted arguments. Default empty array.
  362. * @return array|int List of WP_Site objects, a list of site IDs when 'fields' is set to 'ids',
  363. * or the number of sites when 'count' is passed as a query var.
  364. */
  365. function get_sites( $args = array() ) {
  366. $query = new WP_Site_Query();
  367. return $query->query( $args );
  368. }
  369. /**
  370. * Prepares site data for insertion or update in the database.
  371. *
  372. * @since 5.1.0
  373. *
  374. * @param array $data Associative array of site data passed to the respective function.
  375. * See {@see wp_insert_site()} for the possibly included data.
  376. * @param array $defaults Site data defaults to parse $data against.
  377. * @param WP_Site|null $old_site Optional. Old site object if an update, or null if an insertion.
  378. * Default null.
  379. * @return array|WP_Error Site data ready for a database transaction, or WP_Error in case a validation
  380. * error occurred.
  381. */
  382. function wp_prepare_site_data( $data, $defaults, $old_site = null ) {
  383. // Maintain backward-compatibility with `$site_id` as network ID.
  384. if ( isset( $data['site_id'] ) ) {
  385. if ( ! empty( $data['site_id'] ) && empty( $data['network_id'] ) ) {
  386. $data['network_id'] = $data['site_id'];
  387. }
  388. unset( $data['site_id'] );
  389. }
  390. /**
  391. * Filters passed site data in order to normalize it.
  392. *
  393. * @since 5.1.0
  394. *
  395. * @param array $data Associative array of site data passed to the respective function.
  396. * See {@see wp_insert_site()} for the possibly included data.
  397. */
  398. $data = apply_filters( 'wp_normalize_site_data', $data );
  399. $allowed_data_fields = array( 'domain', 'path', 'network_id', 'registered', 'last_updated', 'public', 'archived', 'mature', 'spam', 'deleted', 'lang_id' );
  400. $data = array_intersect_key( wp_parse_args( $data, $defaults ), array_flip( $allowed_data_fields ) );
  401. $errors = new WP_Error();
  402. /**
  403. * Fires when data should be validated for a site prior to inserting or updating in the database.
  404. *
  405. * Plugins should amend the `$errors` object via its `WP_Error::add()` method.
  406. *
  407. * @since 5.1.0
  408. *
  409. * @param WP_Error $errors Error object to add validation errors to.
  410. * @param array $data Associative array of complete site data. See {@see wp_insert_site()}
  411. * for the included data.
  412. * @param WP_Site|null $old_site The old site object if the data belongs to a site being updated,
  413. * or null if it is a new site being inserted.
  414. */
  415. do_action( 'wp_validate_site_data', $errors, $data, $old_site );
  416. if ( ! empty( $errors->errors ) ) {
  417. return $errors;
  418. }
  419. // Prepare for database.
  420. $data['site_id'] = $data['network_id'];
  421. unset( $data['network_id'] );
  422. return $data;
  423. }
  424. /**
  425. * Normalizes data for a site prior to inserting or updating in the database.
  426. *
  427. * @since 5.1.0
  428. *
  429. * @param array $data Associative array of site data passed to the respective function.
  430. * See {@see wp_insert_site()} for the possibly included data.
  431. * @return array Normalized site data.
  432. */
  433. function wp_normalize_site_data( $data ) {
  434. // Sanitize domain if passed.
  435. if ( array_key_exists( 'domain', $data ) ) {
  436. $data['domain'] = trim( $data['domain'] );
  437. $data['domain'] = preg_replace( '/\s+/', '', sanitize_user( $data['domain'], true ) );
  438. if ( is_subdomain_install() ) {
  439. $data['domain'] = str_replace( '@', '', $data['domain'] );
  440. }
  441. }
  442. // Sanitize path if passed.
  443. if ( array_key_exists( 'path', $data ) ) {
  444. $data['path'] = trailingslashit( '/' . trim( $data['path'], '/' ) );
  445. }
  446. // Sanitize network ID if passed.
  447. if ( array_key_exists( 'network_id', $data ) ) {
  448. $data['network_id'] = (int) $data['network_id'];
  449. }
  450. // Sanitize status fields if passed.
  451. $status_fields = array( 'public', 'archived', 'mature', 'spam', 'deleted' );
  452. foreach ( $status_fields as $status_field ) {
  453. if ( array_key_exists( $status_field, $data ) ) {
  454. $data[ $status_field ] = (int) $data[ $status_field ];
  455. }
  456. }
  457. // Strip date fields if empty.
  458. $date_fields = array( 'registered', 'last_updated' );
  459. foreach ( $date_fields as $date_field ) {
  460. if ( ! array_key_exists( $date_field, $data ) ) {
  461. continue;
  462. }
  463. if ( empty( $data[ $date_field ] ) || '0000-00-00 00:00:00' === $data[ $date_field ] ) {
  464. unset( $data[ $date_field ] );
  465. }
  466. }
  467. return $data;
  468. }
  469. /**
  470. * Validates data for a site prior to inserting or updating in the database.
  471. *
  472. * @since 5.1.0
  473. *
  474. * @param WP_Error $errors Error object, passed by reference. Will contain validation errors if
  475. * any occurred.
  476. * @param array $data Associative array of complete site data. See {@see wp_insert_site()}
  477. * for the included data.
  478. * @param WP_Site|null $old_site The old site object if the data belongs to a site being updated,
  479. * or null if it is a new site being inserted.
  480. */
  481. function wp_validate_site_data( $errors, $data, $old_site = null ) {
  482. // A domain must always be present.
  483. if ( empty( $data['domain'] ) ) {
  484. $errors->add( 'site_empty_domain', __( 'Site domain must not be empty.' ) );
  485. }
  486. // A path must always be present.
  487. if ( empty( $data['path'] ) ) {
  488. $errors->add( 'site_empty_path', __( 'Site path must not be empty.' ) );
  489. }
  490. // A network ID must always be present.
  491. if ( empty( $data['network_id'] ) ) {
  492. $errors->add( 'site_empty_network_id', __( 'Site network ID must be provided.' ) );
  493. }
  494. // Both registration and last updated dates must always be present and valid.
  495. $date_fields = array( 'registered', 'last_updated' );
  496. foreach ( $date_fields as $date_field ) {
  497. if ( empty( $data[ $date_field ] ) ) {
  498. $errors->add( 'site_empty_' . $date_field, __( 'Both registration and last updated dates must be provided.' ) );
  499. break;
  500. }
  501. // Allow '0000-00-00 00:00:00', although it be stripped out at this point.
  502. if ( '0000-00-00 00:00:00' !== $data[ $date_field ] ) {
  503. $month = substr( $data[ $date_field ], 5, 2 );
  504. $day = substr( $data[ $date_field ], 8, 2 );
  505. $year = substr( $data[ $date_field ], 0, 4 );
  506. $valid_date = wp_checkdate( $month, $day, $year, $data[ $date_field ] );
  507. if ( ! $valid_date ) {
  508. $errors->add( 'site_invalid_' . $date_field, __( 'Both registration and last updated dates must be valid dates.' ) );
  509. break;
  510. }
  511. }
  512. }
  513. if ( ! empty( $errors->errors ) ) {
  514. return;
  515. }
  516. // If a new site, or domain/path/network ID have changed, ensure uniqueness.
  517. if ( ! $old_site
  518. || $data['domain'] !== $old_site->domain
  519. || $data['path'] !== $old_site->path
  520. || $data['network_id'] !== $old_site->network_id
  521. ) {
  522. if ( domain_exists( $data['domain'], $data['path'], $data['network_id'] ) ) {
  523. $errors->add( 'site_taken', __( 'Sorry, that site already exists!' ) );
  524. }
  525. }
  526. }
  527. /**
  528. * Runs the initialization routine for a given site.
  529. *
  530. * This process includes creating the site's database tables and
  531. * populating them with defaults.
  532. *
  533. * @since 5.1.0
  534. *
  535. * @global wpdb $wpdb WordPress database abstraction object.
  536. * @global WP_Roles $wp_roles WordPress role management object.
  537. *
  538. * @param int|WP_Site $site_id Site ID or object.
  539. * @param array $args {
  540. * Optional. Arguments to modify the initialization behavior.
  541. *
  542. * @type int $user_id Required. User ID for the site administrator.
  543. * @type string $title Site title. Default is 'Site %d' where %d is the
  544. * site ID.
  545. * @type array $options Custom option $key => $value pairs to use. Default
  546. * empty array.
  547. * @type array $meta Custom site metadata $key => $value pairs to use.
  548. * Default empty array.
  549. * }
  550. * @return true|WP_Error True on success, or error object on failure.
  551. */
  552. function wp_initialize_site( $site_id, array $args = array() ) {
  553. global $wpdb, $wp_roles;
  554. if ( empty( $site_id ) ) {
  555. return new WP_Error( 'site_empty_id', __( 'Site ID must not be empty.' ) );
  556. }
  557. $site = get_site( $site_id );
  558. if ( ! $site ) {
  559. return new WP_Error( 'site_invalid_id', __( 'Site with the ID does not exist.' ) );
  560. }
  561. if ( wp_is_site_initialized( $site ) ) {
  562. return new WP_Error( 'site_already_initialized', __( 'The site appears to be already initialized.' ) );
  563. }
  564. $network = get_network( $site->network_id );
  565. if ( ! $network ) {
  566. $network = get_network();
  567. }
  568. $args = wp_parse_args(
  569. $args,
  570. array(
  571. 'user_id' => 0,
  572. /* translators: %d: Site ID. */
  573. 'title' => sprintf( __( 'Site %d' ), $site->id ),
  574. 'options' => array(),
  575. 'meta' => array(),
  576. )
  577. );
  578. /**
  579. * Filters the arguments for initializing a site.
  580. *
  581. * @since 5.1.0
  582. *
  583. * @param array $args Arguments to modify the initialization behavior.
  584. * @param WP_Site $site Site that is being initialized.
  585. * @param WP_Network $network Network that the site belongs to.
  586. */
  587. $args = apply_filters( 'wp_initialize_site_args', $args, $site, $network );
  588. $orig_installing = wp_installing();
  589. if ( ! $orig_installing ) {
  590. wp_installing( true );
  591. }
  592. $switch = false;
  593. if ( get_current_blog_id() !== $site->id ) {
  594. $switch = true;
  595. switch_to_blog( $site->id );
  596. }
  597. require_once ABSPATH . 'wp-admin/includes/upgrade.php';
  598. // Set up the database tables.
  599. make_db_current_silent( 'blog' );
  600. $home_scheme = 'http';
  601. $siteurl_scheme = 'http';
  602. if ( ! is_subdomain_install() ) {
  603. if ( 'https' === parse_url( get_home_url( $network->site_id ), PHP_URL_SCHEME ) ) {
  604. $home_scheme = 'https';
  605. }
  606. if ( 'https' === parse_url( get_network_option( $network->id, 'siteurl' ), PHP_URL_SCHEME ) ) {
  607. $siteurl_scheme = 'https';
  608. }
  609. }
  610. // Populate the site's options.
  611. populate_options(
  612. array_merge(
  613. array(
  614. 'home' => untrailingslashit( $home_scheme . '://' . $site->domain . $site->path ),
  615. 'siteurl' => untrailingslashit( $siteurl_scheme . '://' . $site->domain . $site->path ),
  616. 'blogname' => wp_unslash( $args['title'] ),
  617. 'admin_email' => '',
  618. 'upload_path' => get_network_option( $network->id, 'ms_files_rewriting' ) ? UPLOADBLOGSDIR . "/{$site->id}/files" : get_blog_option( $network->site_id, 'upload_path' ),
  619. 'blog_public' => (int) $site->public,
  620. 'WPLANG' => get_network_option( $network->id, 'WPLANG' ),
  621. ),
  622. $args['options']
  623. )
  624. );
  625. // Clean blog cache after populating options.
  626. clean_blog_cache( $site );
  627. // Populate the site's roles.
  628. populate_roles();
  629. $wp_roles = new WP_Roles();
  630. // Populate metadata for the site.
  631. populate_site_meta( $site->id, $args['meta'] );
  632. // Remove all permissions that may exist for the site.
  633. $table_prefix = $wpdb->get_blog_prefix();
  634. delete_metadata( 'user', 0, $table_prefix . 'user_level', null, true ); // Delete all.
  635. delete_metadata( 'user', 0, $table_prefix . 'capabilities', null, true ); // Delete all.
  636. // Install default site content.
  637. wp_install_defaults( $args['user_id'] );
  638. // Set the site administrator.
  639. add_user_to_blog( $site->id, $args['user_id'], 'administrator' );
  640. if ( ! user_can( $args['user_id'], 'manage_network' ) && ! get_user_meta( $args['user_id'], 'primary_blog', true ) ) {
  641. update_user_meta( $args['user_id'], 'primary_blog', $site->id );
  642. }
  643. if ( $switch ) {
  644. restore_current_blog();
  645. }
  646. wp_installing( $orig_installing );
  647. return true;
  648. }
  649. /**
  650. * Runs the uninitialization routine for a given site.
  651. *
  652. * This process includes dropping the site's database tables and deleting its uploads directory.
  653. *
  654. * @since 5.1.0
  655. *
  656. * @global wpdb $wpdb WordPress database abstraction object.
  657. *
  658. * @param int|WP_Site $site_id Site ID or object.
  659. * @return true|WP_Error True on success, or error object on failure.
  660. */
  661. function wp_uninitialize_site( $site_id ) {
  662. global $wpdb;
  663. if ( empty( $site_id ) ) {
  664. return new WP_Error( 'site_empty_id', __( 'Site ID must not be empty.' ) );
  665. }
  666. $site = get_site( $site_id );
  667. if ( ! $site ) {
  668. return new WP_Error( 'site_invalid_id', __( 'Site with the ID does not exist.' ) );
  669. }
  670. if ( ! wp_is_site_initialized( $site ) ) {
  671. return new WP_Error( 'site_already_uninitialized', __( 'The site appears to be already uninitialized.' ) );
  672. }
  673. $users = get_users(
  674. array(
  675. 'blog_id' => $site->id,
  676. 'fields' => 'ids',
  677. )
  678. );
  679. // Remove users from the site.
  680. if ( ! empty( $users ) ) {
  681. foreach ( $users as $user_id ) {
  682. remove_user_from_blog( $user_id, $site->id );
  683. }
  684. }
  685. $switch = false;
  686. if ( get_current_blog_id() !== $site->id ) {
  687. $switch = true;
  688. switch_to_blog( $site->id );
  689. }
  690. $uploads = wp_get_upload_dir();
  691. $tables = $wpdb->tables( 'blog' );
  692. /**
  693. * Filters the tables to drop when the site is deleted.
  694. *
  695. * @since MU (3.0.0)
  696. *
  697. * @param string[] $tables Array of names of the site tables to be dropped.
  698. * @param int $site_id The ID of the site to drop tables for.
  699. */
  700. $drop_tables = apply_filters( 'wpmu_drop_tables', $tables, $site->id );
  701. foreach ( (array) $drop_tables as $table ) {
  702. $wpdb->query( "DROP TABLE IF EXISTS `$table`" ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
  703. }
  704. /**
  705. * Filters the upload base directory to delete when the site is deleted.
  706. *
  707. * @since MU (3.0.0)
  708. *
  709. * @param string $basedir Uploads path without subdirectory. @see wp_upload_dir()
  710. * @param int $site_id The site ID.
  711. */
  712. $dir = apply_filters( 'wpmu_delete_blog_upload_dir', $uploads['basedir'], $site->id );
  713. $dir = rtrim( $dir, DIRECTORY_SEPARATOR );
  714. $top_dir = $dir;
  715. $stack = array( $dir );
  716. $index = 0;
  717. while ( $index < count( $stack ) ) {
  718. // Get indexed directory from stack.
  719. $dir = $stack[ $index ];
  720. // phpcs:disable WordPress.PHP.NoSilencedErrors.Discouraged
  721. $dh = @opendir( $dir );
  722. if ( $dh ) {
  723. $file = @readdir( $dh );
  724. while ( false !== $file ) {
  725. if ( '.' === $file || '..' === $file ) {
  726. $file = @readdir( $dh );
  727. continue;
  728. }
  729. if ( @is_dir( $dir . DIRECTORY_SEPARATOR . $file ) ) {
  730. $stack[] = $dir . DIRECTORY_SEPARATOR . $file;
  731. } elseif ( @is_file( $dir . DIRECTORY_SEPARATOR . $file ) ) {
  732. @unlink( $dir . DIRECTORY_SEPARATOR . $file );
  733. }
  734. $file = @readdir( $dh );
  735. }
  736. @closedir( $dh );
  737. }
  738. $index++;
  739. }
  740. $stack = array_reverse( $stack ); // Last added directories are deepest.
  741. foreach ( (array) $stack as $dir ) {
  742. if ( $dir != $top_dir ) {
  743. @rmdir( $dir );
  744. }
  745. }
  746. // phpcs:enable WordPress.PHP.NoSilencedErrors.Discouraged
  747. if ( $switch ) {
  748. restore_current_blog();
  749. }
  750. return true;
  751. }
  752. /**
  753. * Checks whether a site is initialized.
  754. *
  755. * A site is considered initialized when its database tables are present.
  756. *
  757. * @since 5.1.0
  758. *
  759. * @global wpdb $wpdb WordPress database abstraction object.
  760. *
  761. * @param int|WP_Site $site_id Site ID or object.
  762. * @return bool True if the site is initialized, false otherwise.
  763. */
  764. function wp_is_site_initialized( $site_id ) {
  765. global $wpdb;
  766. if ( is_object( $site_id ) ) {
  767. $site_id = $site_id->blog_id;
  768. }
  769. $site_id = (int) $site_id;
  770. /**
  771. * Filters the check for whether a site is initialized before the database is accessed.
  772. *
  773. * Returning a non-null value will effectively short-circuit the function, returning
  774. * that value instead.
  775. *
  776. * @since 5.1.0
  777. *
  778. * @param bool|null $pre The value to return instead. Default null
  779. * to continue with the check.
  780. * @param int $site_id The site ID that is being checked.
  781. */
  782. $pre = apply_filters( 'pre_wp_is_site_initialized', null, $site_id );
  783. if ( null !== $pre ) {
  784. return (bool) $pre;
  785. }
  786. $switch = false;
  787. if ( get_current_blog_id() !== $site_id ) {
  788. $switch = true;
  789. remove_action( 'switch_blog', 'wp_switch_roles_and_user', 1 );
  790. switch_to_blog( $site_id );
  791. }
  792. $suppress = $wpdb->suppress_errors();
  793. $result = (bool) $wpdb->get_results( "DESCRIBE {$wpdb->posts}" );
  794. $wpdb->suppress_errors( $suppress );
  795. if ( $switch ) {
  796. restore_current_blog();
  797. add_action( 'switch_blog', 'wp_switch_roles_and_user', 1, 2 );
  798. }
  799. return $result;
  800. }
  801. /**
  802. * Clean the blog cache
  803. *
  804. * @since 3.5.0
  805. *
  806. * @global bool $_wp_suspend_cache_invalidation
  807. *
  808. * @param WP_Site|int $blog The site object or ID to be cleared from cache.
  809. */
  810. function clean_blog_cache( $blog ) {
  811. global $_wp_suspend_cache_invalidation;
  812. if ( ! empty( $_wp_suspend_cache_invalidation ) ) {
  813. return;
  814. }
  815. if ( empty( $blog ) ) {
  816. return;
  817. }
  818. $blog_id = $blog;
  819. $blog = get_site( $blog_id );
  820. if ( ! $blog ) {
  821. if ( ! is_numeric( $blog_id ) ) {
  822. return;
  823. }
  824. // Make sure a WP_Site object exists even when the site has been deleted.
  825. $blog = new WP_Site(
  826. (object) array(
  827. 'blog_id' => $blog_id,
  828. 'domain' => null,
  829. 'path' => null,
  830. )
  831. );
  832. }
  833. $blog_id = $blog->blog_id;
  834. $domain_path_key = md5( $blog->domain . $blog->path );
  835. wp_cache_delete( $blog_id, 'sites' );
  836. wp_cache_delete( $blog_id, 'site-details' );
  837. wp_cache_delete( $blog_id, 'blog-details' );
  838. wp_cache_delete( $blog_id . 'short', 'blog-details' );
  839. wp_cache_delete( $domain_path_key, 'blog-lookup' );
  840. wp_cache_delete( $domain_path_key, 'blog-id-cache' );
  841. wp_cache_delete( $blog_id, 'blog_meta' );
  842. /**
  843. * Fires immediately after a site has been removed from the object cache.
  844. *
  845. * @since 4.6.0
  846. *
  847. * @param int $id Blog ID.
  848. * @param WP_Site $blog Site object.
  849. * @param string $domain_path_key md5 hash of domain and path.
  850. */
  851. do_action( 'clean_site_cache', $blog_id, $blog, $domain_path_key );
  852. wp_cache_set( 'last_changed', microtime(), 'sites' );
  853. /**
  854. * Fires after the blog details cache is cleared.
  855. *
  856. * @since 3.4.0
  857. * @deprecated 4.9.0 Use {@see 'clean_site_cache'} instead.
  858. *
  859. * @param int $blog_id Blog ID.
  860. */
  861. do_action_deprecated( 'refresh_blog_details', array( $blog_id ), '4.9.0', 'clean_site_cache' );
  862. }
  863. /**
  864. * Adds metadata to a site.
  865. *
  866. * @since 5.1.0
  867. *
  868. * @param int $site_id Site ID.
  869. * @param string $meta_key Metadata name.
  870. * @param mixed $meta_value Metadata value. Must be serializable if non-scalar.
  871. * @param bool $unique Optional. Whether the same key should not be added.
  872. * Default false.
  873. * @return int|false Meta ID on success, false on failure.
  874. */
  875. function add_site_meta( $site_id, $meta_key, $meta_value, $unique = false ) {
  876. return add_metadata( 'blog', $site_id, $meta_key, $meta_value, $unique );
  877. }
  878. /**
  879. * Removes metadata matching criteria from a site.
  880. *
  881. * You can match based on the key, or key and value. Removing based on key and
  882. * value, will keep from removing duplicate metadata with the same key. It also
  883. * allows removing all metadata matching key, if needed.
  884. *
  885. * @since 5.1.0
  886. *
  887. * @param int $site_id Site ID.
  888. * @param string $meta_key Metadata name.
  889. * @param mixed $meta_value Optional. Metadata value. If provided,
  890. * rows will only be removed that match the value.
  891. * Must be serializable if non-scalar. Default empty.
  892. * @return bool True on success, false on failure.
  893. */
  894. function delete_site_meta( $site_id, $meta_key, $meta_value = '' ) {
  895. return delete_metadata( 'blog', $site_id, $meta_key, $meta_value );
  896. }
  897. /**
  898. * Retrieves metadata for a site.
  899. *
  900. * @since 5.1.0
  901. *
  902. * @param int $site_id Site ID.
  903. * @param string $key Optional. The meta key to retrieve. By default,
  904. * returns data for all keys. Default empty.
  905. * @param bool $single Optional. Whether to return a single value.
  906. * This parameter has no effect if `$key` is not specified.
  907. * Default false.
  908. * @return mixed An array of values if `$single` is false.
  909. * The value of meta data field if `$single` is true.
  910. * False for an invalid `$site_id` (non-numeric, zero, or negative value).
  911. * An empty string if a valid but non-existing site ID is passed.
  912. */
  913. function get_site_meta( $site_id, $key = '', $single = false ) {
  914. return get_metadata( 'blog', $site_id, $key, $single );
  915. }
  916. /**
  917. * Updates metadata for a site.
  918. *
  919. * Use the $prev_value parameter to differentiate between meta fields with the
  920. * same key and site ID.
  921. *
  922. * If the meta field for the site does not exist, it will be added.
  923. *
  924. * @since 5.1.0
  925. *
  926. * @param int $site_id Site ID.
  927. * @param string $meta_key Metadata key.
  928. * @param mixed $meta_value Metadata value. Must be serializable if non-scalar.
  929. * @param mixed $prev_value Optional. Previous value to check before updating.
  930. * If specified, only update existing metadata entries with
  931. * this value. Otherwise, update all entries. Default empty.
  932. * @return int|bool Meta ID if the key didn't exist, true on successful update,
  933. * false on failure or if the value passed to the function
  934. * is the same as the one that is already in the database.
  935. */
  936. function update_site_meta( $site_id, $meta_key, $meta_value, $prev_value = '' ) {
  937. return update_metadata( 'blog', $site_id, $meta_key, $meta_value, $prev_value );
  938. }
  939. /**
  940. * Deletes everything from site meta matching meta key.
  941. *
  942. * @since 5.1.0
  943. *
  944. * @param string $meta_key Metadata key to search for when deleting.
  945. * @return bool Whether the site meta key was deleted from the database.
  946. */
  947. function delete_site_meta_by_key( $meta_key ) {
  948. return delete_metadata( 'blog', null, $meta_key, '', true );
  949. }
  950. /**
  951. * Updates the count of sites for a network based on a changed site.
  952. *
  953. * @since 5.1.0
  954. *
  955. * @param WP_Site $new_site The site object that has been inserted, updated or deleted.
  956. * @param WP_Site|null $old_site Optional. If $new_site has been updated, this must be the previous
  957. * state of that site. Default null.
  958. */
  959. function wp_maybe_update_network_site_counts_on_update( $new_site, $old_site = null ) {
  960. if ( null === $old_site ) {
  961. wp_maybe_update_network_site_counts( $new_site->network_id );
  962. return;
  963. }
  964. if ( $new_site->network_id != $old_site->network_id ) {
  965. wp_maybe_update_network_site_counts( $new_site->network_id );
  966. wp_maybe_update_network_site_counts( $old_site->network_id );
  967. }
  968. }
  969. /**
  970. * Triggers actions on site status updates.
  971. *
  972. * @since 5.1.0
  973. *
  974. * @param WP_Site $new_site The site object after the update.
  975. * @param WP_Site|null $old_site Optional. If $new_site has been updated, this must be the previous
  976. * state of that site. Default null.
  977. */
  978. function wp_maybe_transition_site_statuses_on_update( $new_site, $old_site = null ) {
  979. $site_id = $new_site->id;
  980. // Use the default values for a site if no previous state is given.
  981. if ( ! $old_site ) {
  982. $old_site = new WP_Site( new stdClass() );
  983. }
  984. if ( $new_site->spam != $old_site->spam ) {
  985. if ( 1 == $new_site->spam ) {
  986. /**
  987. * Fires when the 'spam' status is added to a site.
  988. *
  989. * @since MU (3.0.0)
  990. *
  991. * @param int $site_id Site ID.
  992. */
  993. do_action( 'make_spam_blog', $site_id );
  994. } else {
  995. /**
  996. * Fires when the 'spam' status is removed from a site.
  997. *
  998. * @since MU (3.0.0)
  999. *
  1000. * @param int $site_id Site ID.
  1001. */
  1002. do_action( 'make_ham_blog', $site_id );
  1003. }
  1004. }
  1005. if ( $new_site->mature != $old_site->mature ) {
  1006. if ( 1 == $new_site->mature ) {
  1007. /**
  1008. * Fires when the 'mature' status is added to a site.
  1009. *
  1010. * @since 3.1.0
  1011. *
  1012. * @param int $site_id Site ID.
  1013. */
  1014. do_action( 'mature_blog', $site_id );
  1015. } else {
  1016. /**
  1017. * Fires when the 'mature' status is removed from a site.
  1018. *
  1019. * @since 3.1.0
  1020. *
  1021. * @param int $site_id Site ID.
  1022. */
  1023. do_action( 'unmature_blog', $site_id );
  1024. }
  1025. }
  1026. if ( $new_site->archived != $old_site->archived ) {
  1027. if ( 1 == $new_site->archived ) {
  1028. /**
  1029. * Fires when the 'archived' status is added to a site.
  1030. *
  1031. * @since MU (3.0.0)
  1032. *
  1033. * @param int $site_id Site ID.
  1034. */
  1035. do_action( 'archive_blog', $site_id );
  1036. } else {
  1037. /**
  1038. * Fires when the 'archived' status is removed from a site.
  1039. *
  1040. * @since MU (3.0.0)
  1041. *
  1042. * @param int $site_id Site ID.
  1043. */
  1044. do_action( 'unarchive_blog', $site_id );
  1045. }
  1046. }
  1047. if ( $new_site->deleted != $old_site->deleted ) {
  1048. if ( 1 == $new_site->deleted ) {
  1049. /**
  1050. * Fires when the 'deleted' status is added to a site.
  1051. *
  1052. * @since 3.5.0
  1053. *
  1054. * @param int $site_id Site ID.
  1055. */
  1056. do_action( 'make_delete_blog', $site_id );
  1057. } else {
  1058. /**
  1059. * Fires when the 'deleted' status is removed from a site.
  1060. *
  1061. * @since 3.5.0
  1062. *
  1063. * @param int $site_id Site ID.
  1064. */
  1065. do_action( 'make_undelete_blog', $site_id );
  1066. }
  1067. }
  1068. if ( $new_site->public != $old_site->public ) {
  1069. /**
  1070. * Fires after the current blog's 'public' setting is updated.
  1071. *
  1072. * @since MU (3.0.0)
  1073. *
  1074. * @param int $site_id Site ID.
  1075. * @param string $value The value of the site status.
  1076. */
  1077. do_action( 'update_blog_public', $site_id, $new_site->public );
  1078. }
  1079. }
  1080. /**
  1081. * Cleans the necessary caches after specific site data has been updated.
  1082. *
  1083. * @since 5.1.0
  1084. *
  1085. * @param WP_Site $new_site The site object after the update.
  1086. * @param WP_Site $old_site The site obejct prior to the update.
  1087. */
  1088. function wp_maybe_clean_new_site_cache_on_update( $new_site, $old_site ) {
  1089. if ( $old_site->domain !== $new_site->domain || $old_site->path !== $new_site->path ) {
  1090. clean_blog_cache( $new_site );
  1091. }
  1092. }
  1093. /**
  1094. * Updates the `blog_public` option for a given site ID.
  1095. *
  1096. * @since 5.1.0
  1097. *
  1098. * @param int $site_id Site ID.
  1099. * @param string $public The value of the site status.
  1100. */
  1101. function wp_update_blog_public_option_on_site_update( $site_id, $public ) {
  1102. // Bail if the site's database tables do not exist (yet).
  1103. if ( ! wp_is_site_initialized( $site_id ) ) {
  1104. return;
  1105. }
  1106. update_blog_option( $site_id, 'blog_public', $public );
  1107. }
  1108. /**
  1109. * Sets the last changed time for the 'sites' cache group.
  1110. *
  1111. * @since 5.1.0
  1112. */
  1113. function wp_cache_set_sites_last_changed() {
  1114. wp_cache_set( 'last_changed', microtime(), 'sites' );
  1115. }
  1116. /**
  1117. * Aborts calls to site meta if it is not supported.
  1118. *
  1119. * @since 5.1.0
  1120. *
  1121. * @global wpdb $wpdb WordPress database abstraction object.
  1122. *
  1123. * @param mixed $check Skip-value for whether to proceed site meta function execution.
  1124. * @return mixed Original value of $check, or false if site meta is not supported.
  1125. */
  1126. function wp_check_site_meta_support_prefilter( $check ) {
  1127. if ( ! is_site_meta_supported() ) {
  1128. /* translators: %s: Database table name. */
  1129. _doing_it_wrong( __FUNCTION__, sprintf( __( 'The %s table is not installed. Please run the network database upgrade.' ), $GLOBALS['wpdb']->blogmeta ), '5.1.0' );
  1130. return false;
  1131. }
  1132. return $check;
  1133. }