+ } 672
+    },
673
+    "node_modules/@awesome-cordova-plugins/http": {
674
+      "version": "6.2.0",
675
+      "resolved": "https://registry.npmjs.org/@awesome-cordova-plugins/http/-/http-6.2.0.tgz",
676
+      "integrity": "sha512-HRLwyiiY1rF9FfjzLJ2lEOWTazpIjHH0cYHiufpURwswgwT1LKVW+mTPlAZsO4bnQ8aJX7clITImbsy+wFFr9g==",
677
+      "dependencies": {
678
+        "@types/cordova": "latest"
679
+      },
680
+      "peerDependencies": {
681
+        "@awesome-cordova-plugins/core": "^6.0.1",
682
+        "rxjs": "^5.5.0 || ^6.5.0 || ^7.3.0"
683
+      }
684
+    },
685
+    "node_modules/@awesome-cordova-plugins/in-app-browser": {
686
+      "version": "6.2.0",
687
+      "resolved": "https://registry.npmjs.org/@awesome-cordova-plugins/in-app-browser/-/in-app-browser-6.2.0.tgz",
688
+      "integrity": "sha512-w2S/s/xvIcPktJLmENnhY+tgrjlZGSOgnuPr2BkwVYCZeGMrlzljmH2bxrF9zPms0CPRb4wuH8+E5FkgYWo80g==",
689
+      "dependencies": {
690
+        "@types/cordova": "latest"
691
+      },
692
+      "peerDependencies": {
693
+        "@awesome-cordova-plugins/core": "^6.0.1",
694
+        "rxjs": "^5.5.0 || ^6.5.0 || ^7.3.0"
695
+      }
696
+    },
697
+    "node_modules/@awesome-cordova-plugins/open-native-settings": {
698
+      "version": "6.2.0",
699
+      "resolved": "https://registry.npmjs.org/@awesome-cordova-plugins/open-native-settings/-/open-native-settings-6.2.0.tgz",
700
+      "integrity": "sha512-ATOYhCfA5ZXWYaMi2n3/tMEp0r6/6eU+x1BXMdQmzoqM3i6uf7Q/KEqe/y/QCQkOaspOxFQTSQjWDqypcUBH+Q==",
701
+      "dependencies": {
702
+        "@types/cordova": "latest"
703
+      },
704
+      "peerDependencies": {
705
+        "@awesome-cordova-plugins/core": "^6.0.1",
706
+        "rxjs": "^5.5.0 || ^6.5.0 || ^7.3.0"
707
+      }
708
+    },
709
+    "node_modules/@awesome-cordova-plugins/push": {
710
+      "version": "6.2.0",
711
+      "resolved": "https://registry.npmjs.org/@awesome-cordova-plugins/push/-/push-6.2.0.tgz",
712
+      "integrity": "sha512-vsQErnXTxyn0JQ6w3Ska1nLxFWBxpWQljZ2nw+FuO9ZbKtUwEmQHroaN9ESCRaxbfd9z9EC5ZRkircyxbETYog==",
713
+      "dependencies": {
714
+        "@types/cordova": "latest"
715
+      },
716
+      "peerDependencies": {
717
+        "@awesome-cordova-plugins/core": "^6.0.1",
718
+        "rxjs": "^5.5.0 || ^6.5.0 || ^7.3.0"
719
+      }
720
+    },
721
+    "node_modules/@awesome-cordova-plugins/sign-in-with-apple": {
722
+      "version": "6.2.0",
723
+      "resolved": "https://registry.npmjs.org/@awesome-cordova-plugins/sign-in-with-apple/-/sign-in-with-apple-6.2.0.tgz",
724
+      "integrity": "sha512-zrMA3QXLJqC8sVtwPS2mIJ/UgBEChuW8pVEDjz/xU5eXmLvxBOsxp8wQkK3rLLEgBksD+YVawuZiqDv28kw5xg==",
725
+      "dependencies": {
726
+        "@types/cordova": "latest"
727
+      },
728
+      "peerDependencies": {
729
+        "@awesome-cordova-plugins/core": "^6.0.1",
730
+        "rxjs": "^5.5.0 || ^6.5.0 || ^7.3.0"
731
+      }
732
+    },
733
+    "node_modules/@awesome-cordova-plugins/streaming-media": {
734
+      "version": "6.2.0",
735
+      "resolved": "https://registry.npmjs.org/@awesome-cordova-plugins/streaming-media/-/streaming-media-6.2.0.tgz",
736
+      "integrity": "sha512-cymtwJON9BQoVcnqMdSR5e/vy7OfsMh9weDm76d7Ns+bZqEmNloTw7Pu0jHGY+dbdLWQ58rEORXUAbnumrSp2A==",
737
+      "dependencies": {
738
+        "@types/cordova": "latest"
739
+      },
740
+      "peerDependencies": {
741
+        "@awesome-cordova-plugins/core": "^6.0.1",
742
+        "rxjs": "^5.5.0 || ^6.5.0 || ^7.3.0"
743
+      }
744
+    },
563 745
     "node_modules/@babel/code-frame": {
564 746
       "version": "7.18.6",
565 747
       "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz",
@@ -3979,6 +4161,11 @@
3979 4161
       "integrity": "sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==",
3980 4162
       "dev": true
3981 4163
     },
4164
+    "node_modules/@types/cordova": {
4165
+      "version": "0.0.34",
4166
+      "resolved": "https://registry.npmjs.org/@types/cordova/-/cordova-0.0.34.tgz",
4167
+      "integrity": "sha512-rkiiTuf/z2wTd4RxFOb+clE7PF4AEJU0hsczbUdkHHBtkUmpWQpEddynNfJYKYtZFJKbq4F+brfekt1kx85IZA=="
4168
+    },
3982 4169
     "node_modules/@types/cors": {
3983 4170
       "version": "2.8.12",
3984 4171
       "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.12.tgz",
@@ -5992,6 +6179,124 @@
5992 6179
         "url": "https://github.com/sponsors/sindresorhus"
5993 6180
       }
5994 6181
     },
6182
+    "node_modules/cordova-open-native-settings": {
6183
+      "version": "1.5.5",
6184
+      "resolved": "https://registry.npmjs.org/cordova-open-native-settings/-/cordova-open-native-settings-1.5.5.tgz",
6185
+      "integrity": "sha512-t3we7dUTUamuxVMl/nE6RBNqEyTtUuxvtjXI/BrevuoBIl2nI9GJpTvD9witJkpzf0isCv3zMuiLd9tP+2+LVw==",
6186
+      "engines": {
6187
+        "cordovaDependencies": {
6188
+          "1.5.0": {
6189
+            "cordova": ">4.0.0"
6190
+          }
6191
+        }
6192
+      }
6193
+    },
6194
+    "node_modules/cordova-plugin-advanced-http": {
6195
+      "version": "3.3.1",
6196
+      "resolved": "https://registry.npmjs.org/cordova-plugin-advanced-http/-/cordova-plugin-advanced-http-3.3.1.tgz",
6197
+      "integrity": "sha512-hESuB3mxIHCUrzb5lm7juda6PSNcC5N8Invizj5wGV2rSldCapiNxMTEpzKR1UVPDDP2XOtBzO0SAYS+3+g/ig==",
6198
+      "engines": [
6199
+        {
6200
+          "name": "cordova",
6201
+          "version": ">=4.0.0"
6202
+        }
6203
+      ]
6204
+    },
6205
+    "node_modules/cordova-plugin-background-mode": {
6206
+      "version": "0.7.3",
6207
+      "resolved": "https://registry.npmjs.org/cordova-plugin-background-mode/-/cordova-plugin-background-mode-0.7.3.tgz",
6208
+      "integrity": "sha512-LsU1v7EgTUROaks+tcQ8TnMzVUcU/TwjDVwj2O/4e4aI2q2ldLGsiZorqKqfqvwh2HoIssmY73OwJk91hQi62w==",
6209
+      "engines": [
6210
+        {
6211
+          "name": "cordova",
6212
+          "version": ">=3.0.0"
6213
+        },
6214
+        {
6215
+          "name": "android-sdk",
6216
+          "version": ">=16"
6217
+        }
6218
+      ]
6219
+    },
6220
+    "node_modules/cordova-plugin-device": {
6221
+      "version": "2.1.0",
6222
+      "resolved": "https://registry.npmjs.org/cordova-plugin-device/-/cordova-plugin-device-2.1.0.tgz",
6223
+      "integrity": "sha512-FU0Lw1jZpuKOgG4v80LrfMAOIMCGfAVPumn7AwaX9S1iU/X3OPZUyoKUgP09q4bxL35IeNPkqNWVKYduAXZ1sg==",
6224
+      "engines": {
6225
+        "cordovaDependencies": {
6226
+          "3.0.0": {
6227
+            "cordova": ">100",
6228
+            "cordova-electron": ">=3.0.0"
6229
+          }
6230
+        }
6231
+      }
6232
+    },
6233
+    "node_modules/cordova-plugin-facebook-connect": {
6234
+      "version": "3.2.0",
6235
+      "resolved": "https://registry.npmjs.org/cordova-plugin-facebook-connect/-/cordova-plugin-facebook-connect-3.2.0.tgz",
6236
+      "integrity": "sha512-7tdHvfJ7r7iN4zqFbNQwJet/UOytvmV6zxr3Cj9NT44/PemjeTn94HlO59I5yLs2Qc+LYc1GsUTnmVcxIqf/SA=="
6237
+    },
6238
+    "node_modules/cordova-plugin-file": {
6239
+      "version": "7.0.0",
6240
+      "resolved": "https://registry.npmjs.org/cordova-plugin-file/-/cordova-plugin-file-7.0.0.tgz",
6241
+      "integrity": "sha512-mSwy9GE5pHq2ZHhu/wYk/VhrwR5VLk+XQsk3+IiiFmDgcPsrVIyELkM2FZKX09cC6i+bJVTFVKUlwteSStj3ow==",
6242
+      "engines": {
6243
+        "cordovaDependencies": {
6244
+          "5.0.0": {
6245
+            "cordova-android": ">=6.3.0"
6246
+          },
6247
+          "7.0.0": {
6248
+            "cordova-android": ">=10.0.0"
6249
+          },
6250
+          "8.0.0": {
6251
+            "cordova": ">100"
6252
+          }
6253
+        }
6254
+      }
6255
+    },
6256
+    "node_modules/cordova-plugin-foreground-service": {
6257
+      "version": "1.1.3",
6258
+      "resolved": "https://registry.npmjs.org/cordova-plugin-foreground-service/-/cordova-plugin-foreground-service-1.1.3.tgz",
6259
+      "integrity": "sha512-/C2Z8w9JIfAaLeXP290msQP9jcHR+CAJYbp6/x+mS21VtvS2Ug0upKsgjEzzSzRnt08Mr8kuwCV7juUd6eCWcw=="
6260
+    },
6261
+    "node_modules/cordova-plugin-google-analytics": {
6262
+      "version": "1.9.2",
6263
+      "resolved": "https://registry.npmjs.org/cordova-plugin-google-analytics/-/cordova-plugin-google-analytics-1.9.2.tgz",
6264
+      "integrity": "sha512-+sYwZIXO6fVFIIJw6IZodQDgs0W/21PDVoGX7SaspUFtleM9vxFctpfQy72RyRA69/ib1HVBMq78j7KiogoKMQ=="
6265
+    },
6266
+    "node_modules/cordova-plugin-inappbrowser": {
6267
+      "version": "5.0.0",
6268
+      "resolved": "https://registry.npmjs.org/cordova-plugin-inappbrowser/-/cordova-plugin-inappbrowser-5.0.0.tgz",
6269
+      "integrity": "sha512-MqnpmUQ/iy6hxtIGDdlIhy8aNi1pNanLATpbnkk7uCqW9YQ4rH/dGK9zESbZ50pUi2A2D2QMjBXNV175TJK5OQ==",
6270
+      "engines": {
6271
+        "cordovaDependencies": {
6272
+          "0.2.3": {
6273
+            "cordova": ">=3.1.0"
6274
+          },
6275
+          "4.0.0": {
6276
+            "cordova": ">=3.1.0",
6277
+            "cordova-ios": ">=4.0.0"
6278
+          },
6279
+          "5.0.0": {
6280
+            "cordova": ">=9.0.0",
6281
+            "cordova-android": ">=9.0.0",
6282
+            "cordova-ios": ">=6.0.0"
6283
+          },
6284
+          "6.0.0": {
6285
+            "cordova": ">100"
6286
+          }
6287
+        }
6288
+      }
6289
+    },
6290
+    "node_modules/cordova-plugin-sign-in-with-apple": {
6291
+      "version": "0.1.2",
6292
+      "resolved": "https://registry.npmjs.org/cordova-plugin-sign-in-with-apple/-/cordova-plugin-sign-in-with-apple-0.1.2.tgz",
6293
+      "integrity": "sha512-SOVa2fwqT5aRGCclgjQIZAA0RsylX9vWtVwNK9sx6kTV65FjZ2CcOygyY//EIo9YNSGzHR+OExhu9EDagyZIpg=="
6294
+    },
6295
+    "node_modules/cordova-plugin-streaming-media": {
6296
+      "version": "2.3.0",
6297
+      "resolved": "https://registry.npmjs.org/cordova-plugin-streaming-media/-/cordova-plugin-streaming-media-2.3.0.tgz",
6298
+      "integrity": "sha512-og7UmXbaWoSrOmo/mZu/c7vKDdUMu2eVrdRMvIJY6qqZ6Fv2BrJvOXm8prVt0xjWqWOMJpQs3DAajX8+N39Cqw=="
6299
+    },
5995 6300
     "node_modules/core-js-compat": {
5996 6301
       "version": "3.26.0",
5997 6302
       "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.26.0.tgz",
@@ -9326,6 +9631,14 @@
9326 9631
         "node": ">=8"
9327 9632
       }
9328 9633
     },
9634
+    "node_modules/install": {
9635
+      "version": "0.8.9",
9636
+      "resolved": "https://registry.npmjs.org/install/-/install-0.8.9.tgz",
9637
+      "integrity": "sha512-y2NNE6v1dGzBMlgbT3D4WalRebVAhj6U5HjpUVLn0UM3cDTCyNwdtZeKL7dkoAg1EeM39uVzqwhgi07o862wcA==",
9638
+      "engines": {
9639
+        "node": ">= 0.10"
9640
+      }
9641
+    },
9329 9642
     "node_modules/internal-slot": {
9330 9643
       "version": "1.0.3",
9331 9644
       "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.3.tgz",
@@ -12386,6 +12699,55 @@
12386 12699
       "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==",
12387 12700
       "dev": true
12388 12701
     },
12702
+    "node_modules/phonegap-plugin-multidex": {
12703
+      "version": "1.0.0",
12704
+      "resolved": "https://registry.npmjs.org/phonegap-plugin-multidex/-/phonegap-plugin-multidex-1.0.0.tgz",
12705
+      "integrity": "sha512-1wvc3iQOQpEBaQbXgLxA2JUiLSQ2azdF/bF29ghXDiQJWSpQ1BF8gSuqttM8WZoj081Ps8OKL0gYxdDBkFNPqA==",
12706
+      "deprecated": "This package is deprecated, see https://blog.phonegap.com/update-for-customers-using-phonegap-and-phonegap-build-cc701c77502c"
12707
+    },
12708
+    "node_modules/phonegap-plugin-push": {
12709
+      "version": "2.3.0",
12710
+      "resolved": "https://registry.npmjs.org/phonegap-plugin-push/-/phonegap-plugin-push-2.3.0.tgz",
12711
+      "integrity": "sha512-0R35BGVvvNzov3fckBspc7snwsIdClIBhI7jUCIa7AgSZy2Lrwm8kPuLTyzMVSNR3MJTxWX6s06aDlVL7EvQ3w==",
12712
+      "deprecated": "This package is deprecated, see https://blog.phonegap.com/update-for-customers-using-phonegap-and-phonegap-build-cc701c77502c",
12713
+      "dependencies": {
12714
+        "install": "^0.8.2"
12715
+      },
12716
+      "engines": {
12717
+        "cordovaDependencies": {
12718
+          "<2.0.0": {
12719
+            "cordova": ">=6.4.0",
12720
+            "cordova-android": ">=6.0.0",
12721
+            "cordova-ios": ">=4.3.0"
12722
+          },
12723
+          "<2.1.2": {
12724
+            "cordova": ">=7.1.0",
12725
+            "cordova-android": ">=6.3.0",
12726
+            "cordova-ios": ">=4.4.0"
12727
+          },
12728
+          "<2.2.0": {
12729
+            "cordova": ">=7.1.0",
12730
+            "cordova-android": ">=6.3.0",
12731
+            "cordova-ios": ">=4.5.0"
12732
+          },
12733
+          "1.8.2": {
12734
+            "cordova": ">=3.6.3",
12735
+            "cordova-android": ">=4.0.0",
12736
+            "cordova-ios": ">=4.1.0"
12737
+          },
12738
+          "2.0.0": {
12739
+            "cordova": ">=7.0.0",
12740
+            "cordova-android": ">=6.2.1",
12741
+            "cordova-ios": ">=4.4.0"
12742
+          },
12743
+          "2.2.0": {
12744
+            "cordova": ">=7.1.0",
12745
+            "cordova-android": ">=7.1.0",
12746
+            "cordova-ios": ">=4.5.0"
12747
+          }
12748
+        }
12749
+      }
12750
+    },
12389 12751
     "node_modules/picocolors": {
12390 12752
       "version": "1.0.0",
12391 12753
       "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",

+ 27 - 1
package.json

@@ -1,5 +1,5 @@
1 1
 {
2
-  "name": "ra100",
2
+  "name": "Active Radio",
3 3
   "version": "0.0.1",
4 4
   "author": "Ionic Framework",
5 5
   "homepage": "https://ionicframework.com/",
@@ -19,6 +19,18 @@
19 19
     "@angular/platform-browser": "^14.0.0",
20 20
     "@angular/platform-browser-dynamic": "^14.0.0",
21 21
     "@angular/router": "^14.0.0",
22
+    "@awesome-cordova-plugins/background-mode": "^6.2.0",
23
+    "@awesome-cordova-plugins/device": "^6.2.0",
24
+    "@awesome-cordova-plugins/facebook": "^6.2.0",
25
+    "@awesome-cordova-plugins/file": "^6.2.0",
26
+    "@awesome-cordova-plugins/foreground-service": "^6.2.0",
27
+    "@awesome-cordova-plugins/google-analytics": "^6.2.0",
28
+    "@awesome-cordova-plugins/http": "^6.2.0",
29
+    "@awesome-cordova-plugins/in-app-browser": "^6.2.0",
30
+    "@awesome-cordova-plugins/open-native-settings": "^6.2.0",
31
+    "@awesome-cordova-plugins/push": "^6.2.0",
32
+    "@awesome-cordova-plugins/sign-in-with-apple": "^6.2.0",
33
+    "@awesome-cordova-plugins/streaming-media": "^6.2.0",
22 34
     "@capacitor/android": "4.4.0",
23 35
     "@capacitor/app": "4.1.0",
24 36
     "@capacitor/core": "^4.4.0",
@@ -32,9 +44,23 @@
32 44
     "@fortawesome/free-regular-svg-icons": "^6.2.0",
33 45
     "@fortawesome/free-solid-svg-icons": "^6.2.0",
34 46
     "@ionic/angular": "^6.1.9",
47
+    "@ionic/storage": "^3.0.6",
35 48
     "@ionic/storage-angular": "^3.0.6",
36 49
     "cocoapods": "^0.0.0",
50
+    "cordova-open-native-settings": "^1.5.5",
51
+    "cordova-plugin-advanced-http": "^3.3.1",
52
+    "cordova-plugin-background-mode": "^0.7.3",
53
+    "cordova-plugin-device": "^2.1.0",
54
+    "cordova-plugin-facebook-connect": "^3.2.0",
55
+    "cordova-plugin-file": "^7.0.0",
56
+    "cordova-plugin-foreground-service": "^1.1.3",
57
+    "cordova-plugin-google-analytics": "^1.9.2",
58
+    "cordova-plugin-inappbrowser": "^5.0.0",
59
+    "cordova-plugin-sign-in-with-apple": "^0.1.2",
60
+    "cordova-plugin-streaming-media": "^2.3.0",
37 61
     "ionicons": "^6.0.3",
62
+    "phonegap-plugin-multidex": "^1.0.0",
63
+    "phonegap-plugin-push": "^2.3.0",
38 64
     "pod-install": "^0.1.38",
39 65
     "rxjs": "~6.6.0",
40 66
     "swiper": "^8.4.4",

+ 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


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

@@ -0,0 +1,10 @@
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">192.168.1.38</domain>
6
+        <domain includeSubdomains="true">google.com</domain>
7
+        <domain includeSubdomains="true">goo.gl</domain>
8
+    </domain-config>
9
+</network-security-config>
10
+

BIN
resources/icon.png


BIN
resources/splash.png


+ 51 - 1
src/app/app.component.ts

@@ -1,4 +1,9 @@
1 1
 import { Component } from '@angular/core';
2
+import { Storage } from '@ionic/storage-angular';
3
+import { InAppBrowser } from '@awesome-cordova-plugins/in-app-browser/ngx';
4
+import { BackgroundMode } from '@awesome-cordova-plugins/background-mode/ngx';
5
+import { GoogleAnalytics } from '@awesome-cordova-plugins/google-analytics/ngx';
6
+import { Platform } from '@ionic/angular';
2 7
 
3 8
 @Component({
4 9
   selector: 'app-root',
@@ -6,5 +11,50 @@ import { Component } from '@angular/core';
6 11
   styleUrls: ['app.component.scss'],
7 12
 })
8 13
 export class AppComponent {
9
-  constructor() {}
14
+    constructor(private storage: Storage, private iab: InAppBrowser, private ga: GoogleAnalytics, private platform: Platform) {
15
+        /*
16
+        this.backgroundMode.setDefaults({
17
+            title: 'MyMusicApp',
18
+            //text: 'Android 8 problem!',
19
+            resume: true,
20
+            hidden: true,
21
+            silent: true
22
+        });
23
+        this.backgroundMode.enable();*/
24
+        console.log("App Component")
25
+            this.storage.create();
26
+            this.platform.ready().then(() => {
27
+                this.ga.startTrackerWithId('G-23DGLKJTFD')
28
+                    .then(() => {
29
+                        console.log('Google analytics is ready now');
30
+                        this.ga.trackView('Outbox')
31
+                            .then(() => {
32
+                            })
33
+                            .catch(
34
+                                error => console.log(error)
35
+                            );
36
+                    }).catch(
37
+                        error => console.log('Google Analytics Error: ' + error)
38
+                    );
39
+
40
+            });
41
+
42
+
43
+    }
44
+    async ngOnInit() {
45
+        // If using a custom driver:
46
+        // await this.storage.defineDriver(MyCustomDriver)
47
+    }
48
+    mailTo() {
49
+        console.log("mail to");
50
+        const browser =  this.iab.create('mailto:fm99mcot@gmail.com', '_system');
51
+    }
52
+    aboutUs() {
53
+        console.log('about us');
54
+        const browser =   this.iab.create('https://dev-web-fm99.mcot.net/privacy-policy-2/', '_system');
55
+    }
56
+    openWeb() {
57
+        console.log('about us');
58
+        const browser =   this.iab.create('https://fm99activeradio.mcot.net/index', '_system');
59
+    }
10 60
 }

+ 14 - 1
src/app/app.module.ts

@@ -14,6 +14,18 @@ import { FontAwesomeModule, FaIconLibrary } from '@fortawesome/angular-fontaweso
14 14
 import { fas } from '@fortawesome/free-solid-svg-icons'
15 15
 import { far } from '@fortawesome/free-regular-svg-icons'
16 16
 import { fab } from '@fortawesome/free-brands-svg-icons'
17
+import { Facebook } from '@awesome-cordova-plugins/facebook/ngx';
18
+import { StreamingMedia } from '@awesome-cordova-plugins/streaming-media/ngx';
19
+import { HTTP } from '@awesome-cordova-plugins/http/ngx';
20
+import { SignInWithApple } from '@awesome-cordova-plugins/sign-in-with-apple/ngx';
21
+import { InAppBrowser } from '@awesome-cordova-plugins/in-app-browser/ngx';
22
+import { BackgroundMode } from '@awesome-cordova-plugins/background-mode/ngx';
23
+import { Push } from '@awesome-cordova-plugins/push/ngx';
24
+import { ForegroundService } from '@awesome-cordova-plugins/foreground-service/ngx';
25
+import { OpenNativeSettings } from '@awesome-cordova-plugins/open-native-settings/ngx';
26
+import { GoogleAnalytics } from '@awesome-cordova-plugins/google-analytics/ngx';
27
+import { IonicStorageModule } from '@ionic/storage-angular';
28
+
17 29
 
18 30
 @NgModule({
19 31
   declarations: [AppComponent],
@@ -23,11 +35,12 @@ import { fab } from '@fortawesome/free-brands-svg-icons'
23 35
     AppRoutingModule, 
24 36
     FontAwesomeModule,
25 37
     HttpClientModule,
38
+    IonicStorageModule.forRoot()
26 39
     // WpdataService,
27 40
     // HttpClient,
28 41
     // HttpHeaders
29 42
   ],
30
-  providers: [{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy }],
43
+  providers: [{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy }, Facebook, StreamingMedia, HTTP, SignInWithApple,  InAppBrowser, BackgroundMode, Push, ForegroundService, OpenNativeSettings, GoogleAnalytics],
31 44
   bootstrap: [AppComponent],
32 45
 })
33 46
 export class AppModule {

+ 17 - 0
src/app/forgot-password/forgot-password-routing.module.ts

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

+ 20 - 0
src/app/forgot-password/forgot-password.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 { ForgotPasswordPageRoutingModule } from './forgot-password-routing.module';
8
+
9
+import { ForgotPasswordPage } from './forgot-password.page';
10
+
11
+@NgModule({
12
+  imports: [
13
+    CommonModule,
14
+    FormsModule,
15
+    IonicModule,
16
+    ForgotPasswordPageRoutingModule
17
+  ],
18
+  declarations: [ForgotPasswordPage]
19
+})
20
+export class ForgotPasswordPageModule {}

+ 37 - 0
src/app/forgot-password/forgot-password.page.html

@@ -0,0 +1,37 @@
1
+<ion-header>
2
+  <ion-toolbar>
3
+      <ion-buttons slot="end">
4
+          <ion-button class='text-white' (click)="dismiss()" color="light">Close</ion-button>
5
+      </ion-buttons>
6
+    <ion-title class="txt-white">Active Radio</ion-title>
7
+  </ion-toolbar>
8
+</ion-header>
9
+<ion-content padding>
10
+  <ion-row>
11
+    <ion-col>
12
+      <div class="ion-text-center"><h1 class="txt-white">ลิมรหัสผ่าน</h1></div>
13
+    </ion-col>
14
+  </ion-row>
15
+  <form>
16
+    <ion-row>
17
+      <ion-col>
18
+        <ion-item lines="full">
19
+          <ion-label position="floating">Email</ion-label>
20
+          <ion-input type="email" name="email" [(ngModel)]="email"  inputmode="email"></ion-input>
21
+        </ion-item>
22
+      </ion-col>
23
+    </ion-row>
24
+    <ion-row>
25
+      <ion-col>
26
+        <ion-button type="submit" color="danger" expand="block" (click)="resetPassword()">ส่งรหัสผ่าน</ion-button>
27
+
28
+      </ion-col>
29
+    </ion-row>
30
+  </form>
31
+  <ion-row>
32
+    <ion-col>
33
+      <small class="txt-white">       
34
+      </small>
35
+    </ion-col>
36
+  </ion-row>
37
+</ion-content>

+ 7 - 0
src/app/forgot-password/forgot-password.page.scss

@@ -0,0 +1,7 @@
1
+ion-content {
2
+    --background: url(/assets/images/bg-01.jpg) no-repeat top center/cover fixed, #fff;
3
+    position: relative;
4
+    height: 100%;
5
+    width: 100%;
6
+    --padding-bottom: 70px;
7
+  }

+ 24 - 0
src/app/forgot-password/forgot-password.page.spec.ts

@@ -0,0 +1,24 @@
1
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
2
+import { IonicModule } from '@ionic/angular';
3
+
4
+import { ForgotPasswordPage } from './forgot-password.page';
5
+
6
+describe('ForgotPasswordPage', () => {
7
+  let component: ForgotPasswordPage;
8
+  let fixture: ComponentFixture<ForgotPasswordPage>;
9
+
10
+  beforeEach(waitForAsync(() => {
11
+    TestBed.configureTestingModule({
12
+      declarations: [ ForgotPasswordPage ],
13
+      imports: [IonicModule.forRoot()]
14
+    }).compileComponents();
15
+
16
+    fixture = TestBed.createComponent(ForgotPasswordPage);
17
+    component = fixture.componentInstance;
18
+    fixture.detectChanges();
19
+  }));
20
+
21
+  it('should create', () => {
22
+    expect(component).toBeTruthy();
23
+  });
24
+});

+ 34 - 0
src/app/forgot-password/forgot-password.page.ts

@@ -0,0 +1,34 @@
1
+import { Component, OnInit } from '@angular/core';
2
+import { ModalController } from '@ionic/angular';
3
+import { WpdataService } from "../services/wpdata.service";
4
+
5
+@Component({
6
+    selector: 'app-forgot-password',
7
+    templateUrl: './forgot-password.page.html',
8
+    styleUrls: ['./forgot-password.page.scss'],
9
+})
10
+export class ForgotPasswordPage implements OnInit {
11
+
12
+    email = "";
13
+
14
+    constructor(public modalController: ModalController, private wp:WpdataService) { }
15
+
16
+    ngOnInit() {
17
+    }
18
+    async resetPassword() {
19
+        if( this.email == "" ) {
20
+            alert("Please Enter Valid Email");
21
+            return;
22
+        }
23
+        let r = await this.wp.resetPassword(this.email);
24
+        alert(r.msg);
25
+    }
26
+
27
+    dismiss() {
28
+        // using the injected ModalController this page
29
+        // can "dismiss" itself and optionally pass back data
30
+        this.modalController.dismiss({
31
+            'dismissed': true
32
+        });
33
+    }
34
+}

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

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

+ 20 - 0
src/app/login/login.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 { LoginPageRoutingModule } from './login-routing.module';
8
+
9
+import { LoginPage } from './login.page';
10
+
11
+@NgModule({
12
+  imports: [
13
+    CommonModule,
14
+    FormsModule,
15
+    IonicModule,
16
+    LoginPageRoutingModule
17
+  ],
18
+  declarations: [LoginPage]
19
+})
20
+export class LoginPageModule {}

+ 63 - 0
src/app/login/login.page.html

@@ -0,0 +1,63 @@
1
+<ion-header>
2
+  <ion-toolbar>
3
+    <ion-buttons slot="end">
4
+      <ion-button (click)="dismiss()" color="light">Close</ion-button>
5
+    </ion-buttons>
6
+    <ion-title class="txt-white">Active Radio</ion-title>
7
+  </ion-toolbar>
8
+</ion-header>
9
+
10
+<ion-content>
11
+  <ion-row>
12
+    <ion-col>
13
+      <div class="ion-text-center">
14
+        <h1 class="txt-white">เข้าสู่ระบบ</h1>
15
+      </div>
16
+    </ion-col>
17
+  </ion-row>
18
+  <ion-row>
19
+    <ion-col>
20
+      <form>
21
+        <ion-item lines="full">
22
+          <ion-label position="floating">Email</ion-label>
23
+          <ion-input type="text" required="true" [(ngModel)]='registerForm.email' name='email' type='email'></ion-input>
24
+        </ion-item>
25
+
26
+        <ion-item lines="full">
27
+          <ion-label position="floating">Password</ion-label>
28
+          <ion-input type="password" required [(ngModel)]='registerForm.password' name='password'></ion-input>
29
+        </ion-item>
30
+      </form>
31
+    </ion-col>
32
+  </ion-row>
33
+  <ion-row>
34
+    <ion-col>
35
+      <ion-button type="submit" color="success" expand="block" (click)="userLogin()">Login</ion-button>
36
+    </ion-col>
37
+  </ion-row>
38
+  <ion-row>
39
+    <ion-col class='ion-text-center'>
40
+      <a (click)="openForgotPassword()" class="small-text">ลืมรหัส</a>
41
+    </ion-col>
42
+    <ion-col class='ion-text-center'>
43
+      <a (click)="openRegister()" class="small-text">สม้ครใหม่</a>
44
+    </ion-col>
45
+  </ion-row>
46
+  <ion-row>
47
+    <ion-col>
48
+      <span class="divider line one-line">หรือ</span>
49
+    </ion-col>
50
+  </ion-row>
51
+  <ion-row>
52
+    <ion-col>
53
+      <ion-button class="log-fb-in-button" expand="block" (click)="doFbLogin()">
54
+        Log in with Facebook
55
+      </ion-button><br>
56
+      <button (click)="appleSignIn()" block [hidden]="isAndroid">
57
+        <img src="assets/images/appleid_button@2x.png">
58
+      </button>
59
+    </ion-col>
60
+  </ion-row>
61
+  <!--
62
+  <ion-button [routerLink]="['/profile']">ไปหน้า profile</ion-button> -->
63
+</ion-content>

+ 54 - 0
src/app/login/login.page.scss

@@ -0,0 +1,54 @@
1
+$social-facebook: #3b5998;
2
+$social-facebook-color: #fff;
3
+$social-facebook-active: #23345a;
4
+$social-facebook-hover: #2d4373;
5
+
6
+ion-content {
7
+  --background: url(/assets/images/bg-01.jpg) no-repeat top center/cover fixed,
8
+    #fff;
9
+  position: relative;
10
+  height: 100%;
11
+  width: 100%;
12
+  --padding-bottom: 70px;
13
+}
14
+
15
+.apple-bnt {
16
+  --vertical-align: middle;
17
+  --padding-start: 10px;
18
+  --padding-end: 10px;
19
+  --background: #2c2c2c;
20
+  --background-hover: rgba(145, 255, 0, 0.89);
21
+}
22
+
23
+.divider {
24
+  display: flex;
25
+
26
+  &:before,
27
+  &:after {
28
+    content: "";
29
+    flex: 1;
30
+  }
31
+}
32
+
33
+.line {
34
+  align-items: center;
35
+  margin: 1em -1em;
36
+  color: #ffffff;
37
+
38
+  &:before,
39
+  &:after {
40
+    height: 1px;
41
+    margin: 0 1em;
42
+  }
43
+}
44
+
45
+.one-line {
46
+  &:before,
47
+  &:after {
48
+    background: #ffffff;
49
+  }
50
+}
51
+
52
+.small-text {
53
+  color: #fff;
54
+}

+ 24 - 0
src/app/login/login.page.spec.ts

@@ -0,0 +1,24 @@
1
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
2
+import { IonicModule } from '@ionic/angular';
3
+
4
+import { LoginPage } from './login.page';
5
+
6
+describe('LoginPage', () => {
7
+  let component: LoginPage;
8
+  let fixture: ComponentFixture<LoginPage>;
9
+
10
+  beforeEach(waitForAsync(() => {
11
+    TestBed.configureTestingModule({
12
+      declarations: [ LoginPage ],
13
+      imports: [IonicModule.forRoot()]
14
+    }).compileComponents();
15
+
16
+    fixture = TestBed.createComponent(LoginPage);
17
+    component = fixture.componentInstance;
18
+    fixture.detectChanges();
19
+  }));
20
+
21
+  it('should create', () => {
22
+    expect(component).toBeTruthy();
23
+  });
24
+});

+ 145 - 0
src/app/login/login.page.ts

@@ -0,0 +1,145 @@
1
+import { Component, OnInit } from '@angular/core';
2
+import { Facebook, FacebookLoginResponse } from '@awesome-cordova-plugins/facebook/ngx';
3
+import { WpdataService } from "../services/wpdata.service";
4
+import { Storage } from '@ionic/storage-angular';
5
+import { Router } from '@angular/router';
6
+import { ModalController, Platform } from '@ionic/angular';
7
+
8
+import { ProfilePage } from '../profile/profile.page';
9
+import { RegisterFormPage } from '../register-form/register-form.page';
10
+import { ForgotPasswordPage } from '../forgot-password/forgot-password.page';
11
+import { SignInWithApple, AppleSignInResponse, AppleSignInErrorResponse, ASAuthorizationAppleIDRequest } from '@awesome-cordova-plugins/sign-in-with-apple/ngx';
12
+
13
+
14
+
15
+@Component({
16
+    selector: 'app-login',
17
+    templateUrl: './login.page.html',
18
+    styleUrls: ['./login.page.scss'],
19
+})
20
+export class LoginPage implements OnInit {
21
+
22
+    registerForm  = {  email: "", password:"" };
23
+    isAndroid = false;
24
+    constructor(private fb: Facebook, private wp: WpdataService, private storage: Storage, private router:Router, public modalController: ModalController, private signInWithApple: SignInWithApple, private platform: Platform) { }
25
+    //constructor() { }
26
+
27
+    ngOnInit() {
28
+        if (this.platform.is('android')) {
29
+            this.isAndroid = true;
30
+            console.log("Android");
31
+        }else {
32
+            console.log("Other");
33
+        }
34
+    }
35
+    async ionViewWillEnter() {
36
+        /*
37
+        let u = await this.storage.get('user');
38
+        console.log(u);
39
+        if( u != null ) {
40
+            //this.router.navigate(['/profile']);
41
+            this.modalController.dismiss({
42
+                'dismissed': true
43
+            });
44
+            const modal = await this.modalController.create({
45
+                component: ProfilePage,
46
+            });
47
+            return await modal.present();
48
+        }*/
49
+    }
50
+    
51
+    async openRegister() {
52
+        this.modalController.dismiss({
53
+            'dismissed': true
54
+        });
55
+        const modal = await this.modalController.create({
56
+            component: RegisterFormPage,
57
+        });
58
+        return await modal.present();
59
+    }
60
+    async openForgotPassword() {
61
+        this.modalController.dismiss({
62
+            'dismissed': true
63
+        });
64
+        const modal = await this.modalController.create({
65
+            component: ForgotPasswordPage,
66
+        });
67
+        return await modal.present();
68
+    }
69
+    async userLogin() {
70
+        console.log(this.registerForm);
71
+        if( this.registerForm.email == "" || this.registerForm.password == "" ) {
72
+            alert("Please enter email and password");
73
+            return;
74
+        }
75
+        let res = await this.wp.loginUser(this.registerForm);
76
+        console.log(res);
77
+        if(res.error == true) {
78
+            alert(res.msg)
79
+        }else {
80
+            alert("Login Success");
81
+            this.storage.set('user', {name: res.output.firstName + " "+res.output.lastName, email: res.output.email});
82
+            this.dismiss();
83
+        }
84
+        
85
+    }
86
+    doFbLogin() {
87
+        this.fb.login(['public_profile',  'email'])
88
+          .then((res: FacebookLoginResponse) =>{ 
89
+              console.log('Logged into Facebook!', res);
90
+
91
+              this.fb.api('me?fields=id,name,email,first_name,last_name,picture.width(720).height(720).as(picture_large)', []).then(async profile => {
92
+                  console.log(profile);
93
+                  let abc = await this.wp.storeFbData(profile);
94
+                  console.log(JSON.parse(abc.data));
95
+                  this.storage.set('user', profile);
96
+                  //this.router.navigate(['/profile']);
97
+                    this.modalController.dismiss({
98
+                        'dismissed': true
99
+                    });
100
+                    const modal = await this.modalController.create({
101
+                        component: ProfilePage,
102
+                    });
103
+                    return await modal.present();
104
+              });
105
+
106
+          })
107
+          .catch(e => console.log('Error logging into Facebook', e));
108
+        
109
+        
110
+    }
111
+    dismiss() {
112
+        // using the injected ModalController this page
113
+        // can "dismiss" itself and optionally pass back data
114
+        this.modalController.dismiss({
115
+            'dismissed': true
116
+        });
117
+    }
118
+    appleSignIn() {
119
+        this.signInWithApple.signin({
120
+            requestedScopes: [
121
+                ASAuthorizationAppleIDRequest.ASAuthorizationScopeFullName,
122
+                ASAuthorizationAppleIDRequest.ASAuthorizationScopeEmail
123
+            ]
124
+        })
125
+            .then((res: AppleSignInResponse) => {
126
+                // https://developer.apple.com/documentation/signinwithapplerestapi/verifying_a_user
127
+                //alert('Send token to apple for verification: ' + res.identityToken);
128
+                console.log("send token");
129
+                console.log(res);
130
+                //alert(res);
131
+                this.wp.storeFbData(res).then( data => {
132
+                    console.log(data);
133
+                }, error => {
134
+                    console.log(error);
135
+                } );
136
+                this.storage.set('user', { name: res.fullName.givenName + " "+res.fullName.familyName, email: res.email });
137
+                this.dismiss();
138
+            })
139
+            .catch((error: AppleSignInErrorResponse) => {
140
+                alert(error.code + ' ' + error.localizedDescription);
141
+                console.error(error);
142
+            });
143
+    }
144
+
145
+}

+ 161 - 3
src/app/navtabs/navtabs.page.ts

@@ -1,4 +1,20 @@
1 1
 import { Component, OnInit } from '@angular/core';
2
+import { Push, PushObject, PushOptions } from '@awesome-cordova-plugins/push/ngx';
3
+import { InAppBrowser } from '@awesome-cordova-plugins/in-app-browser/ngx';
4
+import { ModalController, Platform } from '@ionic/angular';
5
+import { StreamingMedia, StreamingVideoOptions, StreamingAudioOptions } from '@awesome-cordova-plugins/streaming-media/ngx';
6
+
7
+import { BackgroundMode } from '@awesome-cordova-plugins/background-mode/ngx';
8
+import { ForegroundService } from '@awesome-cordova-plugins/foreground-service/ngx';
9
+import { WpdataService } from "../services/wpdata.service";
10
+
11
+import { ProfilePage } from '../profile/profile.page';
12
+import { LoginPage } from '../login/login.page';
13
+import { Storage } from '@ionic/storage-angular';
14
+
15
+
16
+
17
+declare var Media: any;
2 18
 
3 19
 @Component({
4 20
   selector: 'app-navtabs',
@@ -6,10 +22,152 @@ import { Component, OnInit } from '@angular/core';
6 22
   styleUrls: ['./navtabs.page.scss'],
7 23
 })
8 24
 export class NavtabsPage implements OnInit {
25
+    isAudioPlay: boolean = false; 
26
+    audio;
27
+    
28
+    ngOnInit() {
29
+    }
30
+    
31
+    constructor(private streamingMedia: StreamingMedia,  private wp: WpdataService, private storage:Storage, public modalController: ModalController, private push: Push,  private iab: InAppBrowser,  private backgroundMode: BackgroundMode, private platform: Platform, private foregroundService: ForegroundService) {
32
+
33
+        this.platform.ready().then(() => {
34
+            this.backgroundMode.enable();
35
+
36
+            this.backgroundMode.on('enable').subscribe(s => {
37
+                this.backgroundMode.disableWebViewOptimizations();
38
+                this.backgroundMode.disableBatteryOptimizations();
39
+                this.backgroundMode.moveToForeground();
40
+            });
41
+        });
42
+        //this.audio  = new Audio()
43
+        //this.audio.src = 'https://mcotdigital-gvwieh.cdn.byteark.com/radiocdn_edge/fm99.stream_aac/playlist.m3u8';
44
+        //this.audio.load();
45
+
46
+        this.wp.initLiveScore();
47
+        //this.wp.loadStreamLink();
48
+        this.wp.openLogin$.subscribe( data => {
49
+            this.openLogin(data);
50
+        });
51
+        setTimeout(()=>{
52
+
53
+            this.push.hasPermission()
54
+                .then((res: any) => {
55
+
56
+                    if (res.isEnabled) {
57
+                        console.log('We have permission to send push notifications');
58
+                    } else {
59
+                        console.log('We do not have permission to send push notifications');
60
+                    }
61
+
62
+                });
63
+
64
+
65
+            const options: PushOptions = {
66
+                android: {
67
+                    sound: true,
68
+                    iconColor: 'CYAN',
69
+                    icon: 'noticon',
70
+                    forceShow: true,
71
+                },
72
+                ios: {
73
+                    alert: true,
74
+                    badge: true,
75
+                    sound: 'false'
76
+                },
77
+                windows: {},
78
+                browser: {
79
+                    pushServiceURL: 'http://push.api.phonegap.com/v1/push'
80
+                }
81
+            }
82
+
83
+            const pushObject: PushObject = this.push.init(options);
84
+            pushObject.on('notification').subscribe((notification: any) => {
85
+                console.log('Received a notification');
86
+                console.log(notification);
87
+                if( notification.additionalData.link ) {
88
+                    this.iab.create(notification.additionalData.link, "_system");
89
+                }
90
+            });
91
+
92
+            pushObject.on('registration').subscribe((registration: any) => {
93
+                console.log('Device registered', registration.registrationId);
94
+                this.wp.storeAPNSToken(registration.registrationId);
95
+            });
96
+
97
+            pushObject.on('error').subscribe(error => console.error('Error with Push plugin', error));
98
+        }, 2000);
99
+    }
100
+       async openLogin(data) {
101
+
102
+        console.log("open login ..");
103
+        console.log(data);
104
+
105
+        let u = await this.storage.get('user');
106
+        if( u != null ) {
107
+            const modal = await this.modalController.create({
108
+                component: ProfilePage,
109
+                componentProps: {action: data}
110
+            });
111
+            return await modal.present();
112
+        }else {
113
+            const modal = await this.modalController.create({
114
+                component: LoginPage
115
+            });
116
+            return await modal.present();
117
+        }
118
+    }
119
+
120
+
121
+    ionViewWillEnter() {
122
+    }
123
+    async playStreaming() {
124
+
125
+        let user = await this.storage.get('user');
126
+        await this.wp.loadStreamLink();
127
+        let audiourl = await this.storage.get('audiourl');
128
+        if( this.audio == null ) {
129
+            console.log(audiourl);
130
+            //this.audio  = new Audio()
131
+            this.audio = new Media(audiourl);
132
+            //this.audio.src = 'https://mcotdigital-gvwieh.cdn.byteark.com/radiocdn_edge/fm99.stream_aac/playlist.m3u8';
133
+            console.log(audiourl);
134
+            //this.audio.src = audiourl;
135
+            //this.audio.load();
136
+
137
+        }
138
+        console.log(this.audio.src);
139
+        if( this.isAudioPlay == false ) {
140
+            //var media = new Media(audiourl);
141
+            //media.play();
142
+            //this.foregroundService.start('Live Audio Running', 'Background Service', 'noticon', 3, 10);
143
+            this.audio.play();
144
+            if( user != null ) {
145
+                this.wp.appLog({'action': 'play_audio_live', 'email': user.email})
146
+            }
147
+        }else {
148
+            this.audio.pause();
149
+            //this.foregroundService.stop();
150
+            if( user != null ) {
151
+                this.wp.appLog({'action': 'stop_audio_live', 'email': user.email});
152
+            }
153
+        }
154
+        this.isAudioPlay = ! this.isAudioPlay;
9 155
 
10
-  constructor() { }
156
+        console.log("Play Streaming");
157
+    }
158
+    async playVDO() {
159
+        let options: StreamingVideoOptions = {
160
+            successCallback: () => { console.log('Video played') },
161
+            errorCallback: (e) => { console.log('Error streaming') },
162
+            shouldAutoClose: false,
163
+            controls: true,
164
+        };
11 165
 
12
-  ngOnInit() {
13
-  }
166
+        this.streamingMedia.playVideo('https://mcotdigital-gvwieh.cdn.byteark.com/RadioLive/smil:fm99_live.smil/playlist.m3u8', options);
167
+        let user = await this.storage.get('user');
168
+        if( user != null ) {
169
+            this.wp.appLog({'action': 'play_vdo_live', 'email': user.email});
170
+        }
171
+    }
14 172
 
15 173
 }

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

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

+ 20 - 0
src/app/profile/profile.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 { ProfilePageRoutingModule } from './profile-routing.module';
8
+
9
+import { ProfilePage } from './profile.page';
10
+
11
+@NgModule({
12
+  imports: [
13
+    CommonModule,
14
+    FormsModule,
15
+    IonicModule,
16
+    ProfilePageRoutingModule
17
+  ],
18
+  declarations: [ProfilePage]
19
+})
20
+export class ProfilePageModule {}

+ 63 - 0
src/app/profile/profile.page.html

@@ -0,0 +1,63 @@
1
+<ion-header >
2
+  <ion-toolbar>    
3
+      <ion-buttons slot="end">
4
+          <ion-button  (click)="dismiss()" color="light">Close</ion-button>
5
+      </ion-buttons>
6
+    <ion-title class="txt-white">Active Radio</ion-title>    
7
+  </ion-toolbar>
8
+</ion-header>
9
+
10
+<ion-content fullscreen="true" >
11
+    <div *ngIf="(user2 | async) as user; else loading">
12
+  <ion-row>
13
+    <ion-col>
14
+  <div class="card">
15
+    <div class="header">
16
+      <div class="avatar">
17
+          
18
+          <img [src]="$any(user).picture_large.data.url" *ngIf="user['picture_large'] != undefined" />
19
+          <img src="/assets/images/iconfm99.png" *ngIf="user['picture_large'] == undefined" />
20
+      </div>
21
+    </div>
22
+  </div>
23
+  <div class="card-body">
24
+    <div class="user-meta ion-text-center">
25
+      <h3 class="playername">{{ $any(user).name  }}</h3>
26
+    </div>
27
+    <!--
28
+        {{ user }} -->
29
+    <form>
30
+      <ion-item lines="full">
31
+          <ion-label>ชื่อ</ion-label>
32
+        {{ $any(user).name  }}
33
+      </ion-item>
34
+<!--
35
+      <ion-item lines="full">
36
+        <ion-label position="floating">นามสกุล</ion-label>
37
+        <ion-input type="text" required></ion-input>
38
+      </ion-item>-->
39
+
40
+      <ion-item lines="full">
41
+          <ion-label>Email</ion-label>{{ $any(user).email }}
42
+      </ion-item>
43
+<!--
44
+      <ion-item lines="full">
45
+        <ion-label position="floating">Password</ion-label>
46
+        <ion-input type="password" required></ion-input>
47
+      </ion-item> -->
48
+      
49
+      <div class="ion-padding-vertical">
50
+        <!-- <ion-button type="submit" color="success" expand="block">แก้ไบ</ion-button>
51
+        <div class="ion-padding-top"></div>
52
+        <ion-button type="submit" color="danger" expand="block">ยกเลิก</ion-button> -->
53
+        <ion-button type="submit" color="danger" expand="block" (click)="logout()">Log Out</ion-button>
54
+      </div> 
55
+    </form>
56
+  </div>
57
+</ion-col>
58
+</ion-row>
59
+    </div>
60
+<ng-template #loading>
61
+  Loading stuff...
62
+</ng-template>
63
+</ion-content>

+ 68 - 0
src/app/profile/profile.page.scss

@@ -0,0 +1,68 @@
1
+// ion-content {
2
+//   --background: url(/assets/images/bg-01.png) no-repeat top center/cover fixed, #fff;
3
+//   position: relative;
4
+//   height: 100%;
5
+//   width: 100%;
6
+// }
7
+
8
+// ion-toolbar {
9
+//   --background: url(/assets/images/bg-02.png) no-repeat top center/cover fixed, #fff;
10
+//   --background: transparent;
11
+//   --border-width:0;
12
+// }
13
+
14
+ion-content {
15
+  --background: url(/assets/images/bg-01.jpg) no-repeat top center/cover fixed, #fff;
16
+  position: relative;
17
+  height: 100%;
18
+  width: 100%;
19
+  // --padding-bottom: 70px;
20
+}
21
+
22
+.card {
23
+  margin: 0 auto;
24
+
25
+  .header {
26
+    height: 200px;
27
+
28
+    .avatar {
29
+      width: 160px;
30
+      height: 160px;
31
+      position: relative;
32
+      margin: 0 auto;
33
+
34
+      img {
35
+        display: block;
36
+        border-radius: 50%;
37
+        position: absolute;
38
+        bottom: calc(-1*(80px + 4px));
39
+        border: 8px solid #b3e8ff;
40
+        background-color: #fff;
41
+      }
42
+    }
43
+  }
44
+}
45
+
46
+.card-body {
47
+  background-color: #ffffff;
48
+  padding: 30px;
49
+//   height: calc(100vh - (200px + 56px));
50
+//   overflow: hidden;
51
+
52
+  .user-meta {
53
+    padding-top: 40px;
54
+
55
+    .playername {
56
+      font-size: 24px;
57
+      font-weight: 600;
58
+      color: #303940;
59
+    }
60
+
61
+    .country {
62
+      font-size: 90%;
63
+      color: #949ea6;
64
+      text-transform: uppercase;
65
+      margin: 0 auto;
66
+    }
67
+  }
68
+}

+ 24 - 0
src/app/profile/profile.page.spec.ts

@@ -0,0 +1,24 @@
1
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
2
+import { IonicModule } from '@ionic/angular';
3
+
4
+import { ProfilePage } from './profile.page';
5
+
6
+describe('ProfilePage', () => {
7
+  let component: ProfilePage;
8
+  let fixture: ComponentFixture<ProfilePage>;
9
+
10
+  beforeEach(waitForAsync(() => {
11
+    TestBed.configureTestingModule({
12
+      declarations: [ ProfilePage ],
13
+      imports: [IonicModule.forRoot()]
14
+    }).compileComponents();
15
+
16
+    fixture = TestBed.createComponent(ProfilePage);
17
+    component = fixture.componentInstance;
18
+    fixture.detectChanges();
19
+  }));
20
+
21
+  it('should create', () => {
22
+    expect(component).toBeTruthy();
23
+  });
24
+});

+ 41 - 0
src/app/profile/profile.page.ts

@@ -0,0 +1,41 @@
1
+import { Component, OnInit } from '@angular/core';
2
+import { Storage } from '@ionic/storage-angular';
3
+
4
+import { Router } from '@angular/router';
5
+import { ModalController } from '@ionic/angular';
6
+
7
+@Component({
8
+    selector: 'app-profile',
9
+    templateUrl: './profile.page.html',
10
+    styleUrls: ['./profile.page.scss'],
11
+})
12
+export class ProfilePage implements OnInit {
13
+
14
+    user2: any;
15
+    constructor(private storage:Storage, private router:Router, public modalController: ModalController) { }
16
+
17
+    ngOnInit() {
18
+    }
19
+
20
+    async ionViewWillEnter() {
21
+        this.user2 = this.storage.get('user');
22
+        //console.log(this.user);
23
+    }
24
+    async logout() {
25
+        await this.storage.remove('user');
26
+        this.modalController.dismiss({
27
+            'dismissed': true
28
+        });
29
+        //this.router.navigate(['/tabs/home']);
30
+        
31
+    }
32
+
33
+
34
+    dismiss() {
35
+        // using the injected ModalController this page
36
+        // can "dismiss" itself and optionally pass back data
37
+        this.modalController.dismiss({
38
+            'dismissed': true
39
+        });
40
+    }
41
+}

+ 17 - 0
src/app/register-form/register-form-routing.module.ts

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

+ 20 - 0
src/app/register-form/register-form.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 { RegisterFormPageRoutingModule } from './register-form-routing.module';
8
+
9
+import { RegisterFormPage } from './register-form.page';
10
+
11
+@NgModule({
12
+  imports: [
13
+    CommonModule,
14
+    FormsModule,
15
+    IonicModule,
16
+    RegisterFormPageRoutingModule
17
+  ],
18
+  declarations: [RegisterFormPage]
19
+})
20
+export class RegisterFormPageModule {}

+ 59 - 0
src/app/register-form/register-form.page.html

@@ -0,0 +1,59 @@
1
+<ion-header>
2
+  <ion-toolbar>
3
+    <ion-buttons slot="start">
4
+      <ion-back-button></ion-back-button>
5
+    </ion-buttons>
6
+    <ion-title class="txt-white">Active Radio</ion-title>
7
+    <ion-buttons slot="end">
8
+      <ion-button (click)="dismiss()" color="light">Close</ion-button>
9
+    </ion-buttons>
10
+  </ion-toolbar>
11
+</ion-header>
12
+
13
+<ion-content>
14
+  <ion-row>
15
+    <ion-col>
16
+      <div class="ion-text-center"><h1 class="txt-white">ลงทะเบียน Active Radio</h1></div>
17
+    </ion-col>
18
+  </ion-row>
19
+  <ion-row>
20
+    <ion-col>
21
+      <!--
22
+            {{ registerForm }} -->
23
+      <form>
24
+        <ion-text *ngIf="errorText" color="primary" style='background-color:#fff'>
25
+          {{ errorText }}
26
+        </ion-text>
27
+        <ion-item lines="full">
28
+          <ion-label position="floating">ชื่อ</ion-label>
29
+          <ion-input type="text" required [(ngModel)]="registerForm.firstName" name="firstName"></ion-input>
30
+        </ion-item>
31
+
32
+        <ion-item lines="full">
33
+          <ion-label position="floating">นามสกุล</ion-label>
34
+          <ion-input type="text" required [(ngModel)]="registerForm.lastName" name="lastName"></ion-input>
35
+        </ion-item>
36
+
37
+        <ion-item lines="full">
38
+          <ion-label position="floating">Email</ion-label>
39
+          <ion-input type="email" required [(ngModel)]="registerForm.email" name="email"></ion-input>
40
+        </ion-item>
41
+
42
+        <ion-item lines="full">
43
+          <ion-label position="floating">Password</ion-label>
44
+          <ion-input type="password" required [(ngModel)]="registerForm.password" name="password"></ion-input>
45
+        </ion-item>
46
+
47
+        <ion-item lines="full">
48
+          <ion-label position="floating">Confirm Password</ion-label>
49
+          <ion-input type="password" required [(ngModel)]="registerForm.confirmPassword" name="confirmPassword">
50
+          </ion-input>
51
+        </ion-item>
52
+
53
+        <div class="ion-padding-vertical">
54
+          <ion-button type="submit" color="success" expand="block" (click)="register()">ลงทะเบียน</ion-button>
55
+        </div>
56
+      </form>
57
+    </ion-col>
58
+  </ion-row>
59
+</ion-content>

+ 7 - 0
src/app/register-form/register-form.page.scss

@@ -0,0 +1,7 @@
1
+ion-content {
2
+    --background: url(/assets/images/bg-01.jpg) no-repeat top center/cover fixed, #fff;
3
+    position: relative;
4
+    height: 100%;
5
+    width: 100%;
6
+    --padding-bottom: 70px;
7
+  }

+ 24 - 0
src/app/register-form/register-form.page.spec.ts

@@ -0,0 +1,24 @@
1
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
2
+import { IonicModule } from '@ionic/angular';
3
+
4
+import { RegisterFormPage } from './register-form.page';
5
+
6
+describe('RegisterFormPage', () => {
7
+  let component: RegisterFormPage;
8
+  let fixture: ComponentFixture<RegisterFormPage>;
9
+
10
+  beforeEach(waitForAsync(() => {
11
+    TestBed.configureTestingModule({
12
+      declarations: [ RegisterFormPage ],
13
+      imports: [IonicModule.forRoot()]
14
+    }).compileComponents();
15
+
16
+    fixture = TestBed.createComponent(RegisterFormPage);
17
+    component = fixture.componentInstance;
18
+    fixture.detectChanges();
19
+  }));
20
+
21
+  it('should create', () => {
22
+    expect(component).toBeTruthy();
23
+  });
24
+});

+ 44 - 0
src/app/register-form/register-form.page.ts

@@ -0,0 +1,44 @@
1
+import { Component, OnInit } from '@angular/core';
2
+
3
+import { WpdataService } from "../services/wpdata.service";
4
+import { ModalController } from '@ionic/angular';
5
+
6
+@Component({
7
+    selector: 'app-register-form',
8
+    templateUrl: './register-form.page.html',
9
+    styleUrls: ['./register-form.page.scss'],
10
+})
11
+export class RegisterFormPage implements OnInit {
12
+
13
+    registerForm  = {firstName: "", lastName: "", email: "", password:"", confirmPassword: ""};
14
+    errorText = null;
15
+    constructor(private wpdataService: WpdataService, private modalController: ModalController) { }
16
+
17
+    ngOnInit() {
18
+    }
19
+    async register() {
20
+        console.log(this.registerForm);
21
+        if( this.registerForm.password != this.registerForm.confirmPassword ) {
22
+            this.errorText = "Password not matched !!!";
23
+        }else{
24
+            let res = await this.wpdataService.registerUser(this.registerForm);
25
+            if(res.error == true) {
26
+                alert(res.msg)
27
+            }else {
28
+                alert("Register Success");
29
+                this.dismiss();
30
+            
31
+
32
+            }
33
+        }
34
+        
35
+    }
36
+
37
+    dismiss() {
38
+        // using the injected ModalController this page
39
+        // can "dismiss" itself and optionally pass back data
40
+        this.modalController.dismiss({
41
+            'dismissed': true
42
+        });
43
+    }
44
+}

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

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

+ 20 - 0
src/app/register/register.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 { RegisterPageRoutingModule } from './register-routing.module';
8
+
9
+import { RegisterPage } from './register.page';
10
+
11
+@NgModule({
12
+  imports: [
13
+    CommonModule,
14
+    FormsModule,
15
+    IonicModule,
16
+    RegisterPageRoutingModule
17
+  ],
18
+  declarations: [RegisterPage]
19
+})
20
+export class RegisterPageModule {}

+ 9 - 0
src/app/register/register.page.html

@@ -0,0 +1,9 @@
1
+<ion-header>
2
+  <ion-toolbar>
3
+    <ion-title>register</ion-title>
4
+  </ion-toolbar>
5
+</ion-header>
6
+
7
+<ion-content>
8
+
9
+</ion-content>

+ 0 - 0
src/app/register/register.page.scss


+ 24 - 0
src/app/register/register.page.spec.ts

@@ -0,0 +1,24 @@
1
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
2
+import { IonicModule } from '@ionic/angular';
3
+
4
+import { RegisterPage } from './register.page';
5
+
6
+describe('RegisterPage', () => {
7
+  let component: RegisterPage;
8
+  let fixture: ComponentFixture<RegisterPage>;
9
+
10
+  beforeEach(waitForAsync(() => {
11
+    TestBed.configureTestingModule({
12
+      declarations: [ RegisterPage ],
13
+      imports: [IonicModule.forRoot()]
14
+    }).compileComponents();
15
+
16
+    fixture = TestBed.createComponent(RegisterPage);
17
+    component = fixture.componentInstance;
18
+    fixture.detectChanges();
19
+  }));
20
+
21
+  it('should create', () => {
22
+    expect(component).toBeTruthy();
23
+  });
24
+});

+ 15 - 0
src/app/register/register.page.ts

@@ -0,0 +1,15 @@
1
+import { Component, OnInit } from '@angular/core';
2
+
3
+@Component({
4
+  selector: 'app-register',
5
+  templateUrl: './register.page.html',
6
+  styleUrls: ['./register.page.scss'],
7
+})
8
+export class RegisterPage implements OnInit {
9
+
10
+  constructor() { }
11
+
12
+  ngOnInit() {
13
+  }
14
+
15
+}

+ 17 - 0
src/app/reset-password/reset-password-routing.module.ts

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

+ 20 - 0
src/app/reset-password/reset-password.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 { ResetPasswordPageRoutingModule } from './reset-password-routing.module';
8
+
9
+import { ResetPasswordPage } from './reset-password.page';
10
+
11
+@NgModule({
12
+  imports: [
13
+    CommonModule,
14
+    FormsModule,
15
+    IonicModule,
16
+    ResetPasswordPageRoutingModule
17
+  ],
18
+  declarations: [ResetPasswordPage]
19
+})
20
+export class ResetPasswordPageModule {}

+ 40 - 0
src/app/reset-password/reset-password.page.html

@@ -0,0 +1,40 @@
1
+<ion-header>
2
+  <ion-toolbar>
3
+    <ion-buttons slot="start">
4
+      <ion-back-button></ion-back-button>
5
+    </ion-buttons>
6
+    <ion-title>เอฟเอ็ม 99 Active Radio</ion-title>
7
+  </ion-toolbar>
8
+</ion-header>
9
+<ion-content padding>
10
+  <ion-row>
11
+    <ion-col>
12
+      <h1 class="txt-white">กรุณาใส่รหัสผ่านใหม่</h1>
13
+    </ion-col>
14
+  </ion-row>
15
+  <form>
16
+    <ion-row>
17
+      <ion-col>
18
+        <ion-item lines="full">
19
+          <ion-label position="floating">Password</ion-label>
20
+          <ion-input type="password" required></ion-input>
21
+        </ion-item>
22
+
23
+        <ion-item lines="full">
24
+          <ion-label position="floating">Confirm Password</ion-label>
25
+          <ion-input type="password" required></ion-input>
26
+        </ion-item>
27
+      </ion-col>
28
+    </ion-row>
29
+    <ion-row>
30
+      <ion-col>
31
+        <ion-button type="submit" color="success" expand="block">Send</ion-button>
32
+
33
+      </ion-col>
34
+    </ion-row>
35
+  </form>
36
+  <ion-row>
37
+    <ion-col>     
38
+    </ion-col>
39
+  </ion-row>
40
+</ion-content>

+ 7 - 0
src/app/reset-password/reset-password.page.scss

@@ -0,0 +1,7 @@
1
+ion-content {
2
+    --background: url(/assets/images/bg-01.jpg) no-repeat top center/cover fixed, #fff;
3
+    position: relative;
4
+    height: 100%;
5
+    width: 100%;
6
+    --padding-bottom: 70px;
7
+  }

+ 24 - 0
src/app/reset-password/reset-password.page.spec.ts

@@ -0,0 +1,24 @@
1
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
2
+import { IonicModule } from '@ionic/angular';
3
+
4
+import { ResetPasswordPage } from './reset-password.page';
5
+
6
+describe('ResetPasswordPage', () => {
7
+  let component: ResetPasswordPage;
8
+  let fixture: ComponentFixture<ResetPasswordPage>;
9
+
10
+  beforeEach(waitForAsync(() => {
11
+    TestBed.configureTestingModule({
12
+      declarations: [ ResetPasswordPage ],
13
+      imports: [IonicModule.forRoot()]
14
+    }).compileComponents();
15
+
16
+    fixture = TestBed.createComponent(ResetPasswordPage);
17
+    component = fixture.componentInstance;
18
+    fixture.detectChanges();
19
+  }));
20
+
21
+  it('should create', () => {
22
+    expect(component).toBeTruthy();
23
+  });
24
+});

+ 15 - 0
src/app/reset-password/reset-password.page.ts

@@ -0,0 +1,15 @@
1
+import { Component, OnInit } from '@angular/core';
2
+
3
+@Component({
4
+  selector: 'app-reset-password',
5
+  templateUrl: './reset-password.page.html',
6
+  styleUrls: ['./reset-password.page.scss'],
7
+})
8
+export class ResetPasswordPage implements OnInit {
9
+
10
+  constructor() { }
11
+
12
+  ngOnInit() {
13
+  }
14
+
15
+}

+ 199 - 5
src/app/services/wpdata.service.ts

@@ -1,15 +1,31 @@
1
-import { Injectable } from '@angular/core';
1
+import { Injectable, EventEmitter } from '@angular/core';
2
+
2 3
 import { Observable } from 'rxjs';
3 4
 import { map } from 'rxjs/operators';
4 5
 import { HttpClient, HttpHeaders } from '@angular/common/http';
5 6
 import { Storage } from '@ionic/storage-angular';
7
+import { HTTP } from '@awesome-cordova-plugins/http/ngx';
8
+
6 9
 
7 10
 @Injectable({
8 11
   providedIn: 'root'
9 12
 })
10 13
 export class WpdataService {
14
+    APP_URL = "https://dev-app-fm99.mcot.net/api";
15
+    //APP_URL = "http://192.168.1.38:5000/api";
11 16
 
12
-  constructor(private http: HttpClient) { }
17
+    public login$: EventEmitter<any>;
18
+    public openLogin$: EventEmitter<any>;
19
+    public scoreAlert$: EventEmitter<any>;
20
+    public liveScoreId;
21
+    constructor(private httpc: HttpClient, private http: HTTP, private storage:Storage) {
22
+        this.login$ = new EventEmitter();
23
+        this.openLogin$ = new EventEmitter();
24
+        this.scoreAlert$ = new EventEmitter();
25
+        this.storage.set('lastUpdate', new Date());
26
+        this.loadStreamLink();
27
+
28
+    }
13 29
 
14 30
   // getAllNews() {
15 31
   //   return this.http.get(
@@ -17,7 +33,7 @@ export class WpdataService {
17 33
   //   );
18 34
   // }
19 35
   getAllNews() {
20
-    return this.http.get(
36
+    return this.httpc.get(
21 37
       "https://fm99activeradio.mcot.net/wp-json/wp/v2/news?_embed"
22 38
     );
23 39
   }
@@ -25,13 +41,191 @@ export class WpdataService {
25 41
   getNewsDetail(id) {
26 42
     let url = 'https://fm99activeradio.mcot.net/'
27 43
     const route = url + 'wp-json/wp/v2/' + 'news/' + id + '?_embed';
28
-    return this.http.get(route);
44
+    return this.httpc.get(route);
29 45
   }
30 46
 
31 47
   getAllYoutube() {
32
-    return this.http.get(
48
+    return this.httpc.get(
33 49
        "../assets/api/youtube-data.json"
34 50
      // "https://youtube.googleapis.com/youtube/v3/search?part=snippet&channelId=UCfZdHbI3pIN4uselPFzzbOg&maxResults=5&order=date&key=AIzaSyDsRrS_dUxT_3PFaMZIBBdrbl37p_fqrNY"
35 51
     );
36 52
   }
53
+    appLog(form) {
54
+
55
+        this.http.setDataSerializer('json');
56
+        return this.http.post(this.APP_URL + "/applog", form, {})
57
+            .then( res => {
58
+                console.log(res);
59
+                return JSON.parse(res.data);
60
+            },
61
+                error => {
62
+                    console.log(error);
63
+                    return error;
64
+                });
65
+    }
66
+     async initLiveScore() {
67
+        return;
68
+
69
+        let oldText = "";
70
+
71
+        /*
72
+        let abc = setInterval(async () => {
73
+
74
+            this.localNotifications.schedule([
75
+                { id: 1,  title: 'My first notification',   smallIcon: 'res://noti_default',  icon: 'res://ic_launcher'
76
+                },
77
+                { id: 2, title: 'My second notification',  smallIcon: 'res://noti_default',   icon: 'res://ic_launcher'
78
+                }]);
79
+        }, 5*1000);*/
80
+    }
81
+    clearLiveScore() {
82
+        if(this.liveScoreId) {
83
+            clearInterval(this.liveScoreId);
84
+        }
85
+    }
86
+    getLiveScore() {
87
+        return this.http.get(
88
+          this.APP_URL +  "/livescore", {}, {}
89
+        );
90
+    }
91
+
92
+    getFixture() {
93
+        return this.http.get(
94
+          this.APP_URL +  "/fixtures", {}, {}
95
+        );
96
+    }
97
+    getTables(group) {
98
+        return this.http.get(
99
+          this.APP_URL +  "/tables", {'group': group}, {}
100
+        );
101
+    }
102
+    getPassMatches() {
103
+        return this.http.get(
104
+          this.APP_URL +  "/pass_matches", {}, {}
105
+        );
106
+    }
107
+    matchEvents(match_id) {
108
+        return this.http.get(
109
+          this.APP_URL +  "/events", {'match_id': match_id}, {}
110
+        );
111
+    }
112
+    lineups(match_id) {
113
+        return this.http.get(
114
+          this.APP_URL +  "/lineups", {'match_id': match_id}, {}
115
+        );
116
+    }
117
+    getRemoteData() {
118
+        return this.http.get(
119
+            "https://dev-web-fm99.mcot.net/wp-json/wp/v2/banner?_embed", {}, {}
120
+        );
121
+    }
122
+    getPodcastData() {
123
+        return this.http.get(
124
+            "https://dev-web-fm99.mcot.net/wp-json/wp/v2/podcast?_embed", {}, {}
125
+        );
126
+    }
127
+
128
+    getPodcastAll() {
129
+        return this.http.get(
130
+            "https://dev-web-fm99.mcot.net/wp-json/wp/v2/podcast?_embed", {}, {}
131
+        );
132
+    }
133
+
134
+    loadStreamLink() {
135
+        return this.http.get("https://dev-web-fm99.mcot.net/livetream.json", {}, {})
136
+            .then(res => {
137
+                let data = JSON.parse(res.data);
138
+                console.log(data);
139
+                this.storage.set('audiourl', data['fm99treamurl']['audiourl']);
140
+                this.storage.set('videourl', data['fm99treamurl']['videourl']);
141
+
142
+            }, error=> {
143
+                console.log(error);
144
+            });
145
+    }
146
+    getNewsData() {
147
+        return this.http.get(
148
+            "https://dev-web-fm99.mcot.net/wp-json/wp/v2/news?_embed", {}, {}
149
+        );
150
+    }
151
+    getCurrentProgram() {
152
+        return this.http.get("https://dev-app-fm99.mcot.net/api/current_program", {}, {});
153
+    }
154
+    getPostDetails(id) {
155
+        let url  = 'https://dev-web-fm99.mcot.net/'
156
+        const route = url + 'wp-json/wp/v2/' + 'podcast/'+id+'?_embed';
157
+        // const podAPI = "https://dev-web-fm99.mcot.net/podcast/"+id+"?_embed";
158
+        console.log(route);
159
+        return this.http.get(route, {}, {});
160
+
161
+    }
162
+    getNewsDetailNative(id) {
163
+        let url  = 'https://dev-web-fm99.mcot.net/'
164
+        const route = url + 'wp-json/wp/v2/' + 'news/' + id + '?_embed';
165
+        // const podAPI = "https://dev-web-fm99.mcot.net/podcast/"+id+"?_embed";
166
+        return this.http.get(route, {}, {});
167
+    }
168
+    storeAPNSToken(token){
169
+        this.http.setDataSerializer('json');
170
+        return this.http.post(this.APP_URL + "/reg_apns", {'token': token}, {})
171
+        .then( res => {
172
+            return res;
173
+        }, error => {
174
+            return error;
175
+        } );
176
+    }
177
+
178
+    storeFbData(data) {
179
+        console.log("store fb");
180
+        console.log(data);
181
+        this.http.setDataSerializer('json');
182
+        return this.http.post(this.APP_URL + "/store_fb", data, {})
183
+        .then( res => {
184
+            console.log(res);
185
+            return res;
186
+        },
187
+        error => {
188
+            console.log(error);
189
+            return error;
190
+        });
191
+    }
192
+    registerUser(form) {
193
+
194
+        this.http.setDataSerializer('json');
195
+        return this.http.post(this.APP_URL + "/register", form, {})
196
+        .then( res => {
197
+            console.log(res);
198
+            return JSON.parse(res.data);
199
+        },
200
+        error => {
201
+            console.log(error);
202
+            return error;
203
+        });
204
+    }
205
+    loginUser(form) {
206
+
207
+        this.http.setDataSerializer('json');
208
+        return this.http.post(this.APP_URL + "/user_login", form, {})
209
+        .then( res => {
210
+            console.log(res);
211
+            return JSON.parse(res.data);
212
+        },
213
+        error => {
214
+            console.log(error);
215
+            return error;
216
+        });
217
+    }
218
+    resetPassword(email) {
219
+
220
+        this.http.setDataSerializer('json');
221
+        return this.http.get(this.APP_URL + "/resetpass/"+email, {}, {})
222
+        .then( res => {
223
+            console.log(res);
224
+            return JSON.parse(res.data);
225
+        },
226
+        error => {
227
+            console.log(error);
228
+            return error;
229
+        });
230
+    }
37 231
 }

tum/whitesports - Gogs: Simplico Git Service

Bez popisu

nav-menus.php 46KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155
  1. <?php
  2. /**
  3. * WordPress Administration for Navigation Menus
  4. * Interface functions
  5. *
  6. * @version 2.0.0
  7. *
  8. * @package WordPress
  9. * @subpackage Administration
  10. */
  11. /** Load WordPress Administration Bootstrap */
  12. require_once __DIR__ . '/admin.php';
  13. // Load all the nav menu interface functions.
  14. require_once ABSPATH . 'wp-admin/includes/nav-menu.php';
  15. if ( ! current_theme_supports( 'menus' ) && ! current_theme_supports( 'widgets' ) ) {
  16. wp_die( __( 'Your theme does not support navigation menus or widgets.' ) );
  17. }
  18. // Permissions check.
  19. if ( ! current_user_can( 'edit_theme_options' ) ) {
  20. wp_die(
  21. '<h1>' . __( 'You need a higher level of permission.' ) . '</h1>' .
  22. '<p>' . __( 'Sorry, you are not allowed to edit theme options on this site.' ) . '</p>',
  23. 403
  24. );
  25. }
  26. wp_enqueue_script( 'nav-menu' );
  27. if ( wp_is_mobile() ) {
  28. wp_enqueue_script( 'jquery-touch-punch' );
  29. }
  30. // Container for any messages displayed to the user.
  31. $messages = array();
  32. // Container that stores the name of the active menu.
  33. $nav_menu_selected_title = '';
  34. // The menu id of the current menu being edited.
  35. $nav_menu_selected_id = isset( $_REQUEST['menu'] ) ? (int) $_REQUEST['menu'] : 0;
  36. // Get existing menu locations assignments.
  37. $locations = get_registered_nav_menus();
  38. $menu_locations = get_nav_menu_locations();
  39. $num_locations = count( array_keys( $locations ) );
  40. // Allowed actions: add, update, delete.
  41. $action = isset( $_REQUEST['action'] ) ? $_REQUEST['action'] : 'edit';
  42. /*
  43. * If a JSON blob of navigation menu data is found, expand it and inject it
  44. * into `$_POST` to avoid PHP `max_input_vars` limitations. See #14134.
  45. */
  46. _wp_expand_nav_menu_post_data();
  47. switch ( $action ) {
  48. case 'add-menu-item':
  49. check_admin_referer( 'add-menu_item', 'menu-settings-column-nonce' );
  50. if ( isset( $_REQUEST['nav-menu-locations'] ) ) {
  51. set_theme_mod( 'nav_menu_locations', array_map( 'absint', $_REQUEST['menu-locations'] ) );
  52. } elseif ( isset( $_REQUEST['menu-item'] ) ) {
  53. wp_save_nav_menu_items( $nav_menu_selected_id, $_REQUEST['menu-item'] );
  54. }
  55. break;
  56. case 'move-down-menu-item':
  57. // Moving down a menu item is the same as moving up the next in order.
  58. check_admin_referer( 'move-menu_item' );
  59. $menu_item_id = isset( $_REQUEST['menu-item'] ) ? (int) $_REQUEST['menu-item'] : 0;
  60. if ( is_nav_menu_item( $menu_item_id ) ) {
  61. $menus = isset( $_REQUEST['menu'] ) ? array( (int) $_REQUEST['menu'] ) : wp_get_object_terms( $menu_item_id, 'nav_menu', array( 'fields' => 'ids' ) );
  62. if ( ! is_wp_error( $menus ) && ! empty( $menus[0] ) ) {
  63. $menu_id = (int) $menus[0];
  64. $ordered_menu_items = wp_get_nav_menu_items( $menu_id );
  65. $menu_item_data = (array) wp_setup_nav_menu_item( get_post( $menu_item_id ) );
  66. // Set up the data we need in one pass through the array of menu items.
  67. $dbids_to_orders = array();
  68. $orders_to_dbids = array();
  69. foreach ( (array) $ordered_menu_items as $ordered_menu_item_object ) {
  70. if ( isset( $ordered_menu_item_object->ID ) ) {
  71. if ( isset( $ordered_menu_item_object->menu_order ) ) {
  72. $dbids_to_orders[ $ordered_menu_item_object->ID ] = $ordered_menu_item_object->menu_order;
  73. $orders_to_dbids[ $ordered_menu_item_object->menu_order ] = $ordered_menu_item_object->ID;
  74. }
  75. }
  76. }
  77. // Get next in order.
  78. if ( isset( $orders_to_dbids[ $dbids_to_orders[ $menu_item_id ] + 1 ] ) ) {
  79. $next_item_id = $orders_to_dbids[ $dbids_to_orders[ $menu_item_id ] + 1 ];
  80. $next_item_data = (array) wp_setup_nav_menu_item( get_post( $next_item_id ) );
  81. // If not siblings of same parent, bubble menu item up but keep order.
  82. if ( ! empty( $menu_item_data['menu_item_parent'] )
  83. && ( empty( $next_item_data['menu_item_parent'] )
  84. || (int) $next_item_data['menu_item_parent'] !== (int) $menu_item_data['menu_item_parent'] )
  85. ) {
  86. if ( in_array( (int) $menu_item_data['menu_item_parent'], $orders_to_dbids, true ) ) {
  87. $parent_db_id = (int) $menu_item_data['menu_item_parent'];
  88. } else {
  89. $parent_db_id = 0;
  90. }
  91. $parent_object = wp_setup_nav_menu_item( get_post( $parent_db_id ) );
  92. if ( ! is_wp_error( $parent_object ) ) {
  93. $parent_data = (array) $parent_object;
  94. $menu_item_data['menu_item_parent'] = $parent_data['menu_item_parent'];
  95. update_post_meta( $menu_item_data['ID'], '_menu_item_menu_item_parent', (int) $menu_item_data['menu_item_parent'] );
  96. }
  97. // Make menu item a child of its next sibling.
  98. } else {
  99. $next_item_data['menu_order'] = $next_item_data['menu_order'] - 1;
  100. $menu_item_data['menu_order'] = $menu_item_data['menu_order'] + 1;
  101. $menu_item_data['menu_item_parent'] = $next_item_data['ID'];
  102. update_post_meta( $menu_item_data['ID'], '_menu_item_menu_item_parent', (int) $menu_item_data['menu_item_parent'] );
  103. wp_update_post( $menu_item_data );
  104. wp_update_post( $next_item_data );
  105. }
  106. // The item is last but still has a parent, so bubble up.
  107. } elseif ( ! empty( $menu_item_data['menu_item_parent'] )
  108. && in_array( (int) $menu_item_data['menu_item_parent'], $orders_to_dbids, true )
  109. ) {
  110. $menu_item_data['menu_item_parent'] = (int) get_post_meta( $menu_item_data['menu_item_parent'], '_menu_item_menu_item_parent', true );
  111. update_post_meta( $menu_item_data['ID'], '_menu_item_menu_item_parent', (int) $menu_item_data['menu_item_parent'] );
  112. }
  113. }
  114. }
  115. break;
  116. case 'move-up-menu-item':
  117. check_admin_referer( 'move-menu_item' );
  118. $menu_item_id = isset( $_REQUEST['menu-item'] ) ? (int) $_REQUEST['menu-item'] : 0;
  119. if ( is_nav_menu_item( $menu_item_id ) ) {
  120. if ( isset( $_REQUEST['menu'] ) ) {
  121. $menus = array( (int) $_REQUEST['menu'] );
  122. } else {
  123. $menus = wp_get_object_terms( $menu_item_id, 'nav_menu', array( 'fields' => 'ids' ) );
  124. }
  125. if ( ! is_wp_error( $menus ) && ! empty( $menus[0] ) ) {
  126. $menu_id = (int) $menus[0];
  127. $ordered_menu_items = wp_get_nav_menu_items( $menu_id );
  128. $menu_item_data = (array) wp_setup_nav_menu_item( get_post( $menu_item_id ) );
  129. // Set up the data we need in one pass through the array of menu items.
  130. $dbids_to_orders = array();
  131. $orders_to_dbids = array();
  132. foreach ( (array) $ordered_menu_items as $ordered_menu_item_object ) {
  133. if ( isset( $ordered_menu_item_object->ID ) ) {
  134. if ( isset( $ordered_menu_item_object->menu_order ) ) {
  135. $dbids_to_orders[ $ordered_menu_item_object->ID ] = $ordered_menu_item_object->menu_order;
  136. $orders_to_dbids[ $ordered_menu_item_object->menu_order ] = $ordered_menu_item_object->ID;
  137. }
  138. }
  139. }
  140. // If this menu item is not first.
  141. if ( ! empty( $dbids_to_orders[ $menu_item_id ] )
  142. && ! empty( $orders_to_dbids[ $dbids_to_orders[ $menu_item_id ] - 1 ] )
  143. ) {
  144. // If this menu item is a child of the previous.
  145. if ( ! empty( $menu_item_data['menu_item_parent'] )
  146. && in_array( (int) $menu_item_data['menu_item_parent'], array_keys( $dbids_to_orders ), true )
  147. && isset( $orders_to_dbids[ $dbids_to_orders[ $menu_item_id ] - 1 ] )
  148. && ( (int) $menu_item_data['menu_item_parent'] === $orders_to_dbids[ $dbids_to_orders[ $menu_item_id ] - 1 ] )
  149. ) {
  150. if ( in_array( (int) $menu_item_data['menu_item_parent'], $orders_to_dbids, true ) ) {
  151. $parent_db_id = (int) $menu_item_data['menu_item_parent'];
  152. } else {
  153. $parent_db_id = 0;
  154. }
  155. $parent_object = wp_setup_nav_menu_item( get_post( $parent_db_id ) );
  156. if ( ! is_wp_error( $parent_object ) ) {
  157. $parent_data = (array) $parent_object;
  158. /*
  159. * If there is something before the parent and parent a child of it,
  160. * make menu item a child also of it.
  161. */
  162. if ( ! empty( $dbids_to_orders[ $parent_db_id ] )
  163. && ! empty( $orders_to_dbids[ $dbids_to_orders[ $parent_db_id ] - 1 ] )
  164. && ! empty( $parent_data['menu_item_parent'] )
  165. ) {
  166. $menu_item_data['menu_item_parent'] = $parent_data['menu_item_parent'];
  167. /*
  168. * Else if there is something before parent and parent not a child of it,
  169. * make menu item a child of that something's parent
  170. */
  171. } elseif ( ! empty( $dbids_to_orders[ $parent_db_id ] )
  172. && ! empty( $orders_to_dbids[ $dbids_to_orders[ $parent_db_id ] - 1 ] )
  173. ) {
  174. $_possible_parent_id = (int) get_post_meta( $orders_to_dbids[ $dbids_to_orders[ $parent_db_id ] - 1 ], '_menu_item_menu_item_parent', true );
  175. if ( in_array( $_possible_parent_id, array_keys( $dbids_to_orders ), true ) ) {
  176. $menu_item_data['menu_item_parent'] = $_possible_parent_id;
  177. } else {
  178. $menu_item_data['menu_item_parent'] = 0;
  179. }
  180. // Else there isn't something before the parent.
  181. } else {
  182. $menu_item_data['menu_item_parent'] = 0;
  183. }
  184. // Set former parent's [menu_order] to that of menu-item's.
  185. $parent_data['menu_order'] = $parent_data['menu_order'] + 1;
  186. // Set menu-item's [menu_order] to that of former parent.
  187. $menu_item_data['menu_order'] = $menu_item_data['menu_order'] - 1;
  188. // Save changes.
  189. update_post_meta( $menu_item_data['ID'], '_menu_item_menu_item_parent', (int) $menu_item_data['menu_item_parent'] );
  190. wp_update_post( $menu_item_data );
  191. wp_update_post( $parent_data );
  192. }
  193. // Else this menu item is not a child of the previous.
  194. } elseif ( empty( $menu_item_data['menu_order'] )
  195. || empty( $menu_item_data['menu_item_parent'] )
  196. || ! in_array( (int) $menu_item_data['menu_item_parent'], array_keys( $dbids_to_orders ), true )
  197. || empty( $orders_to_dbids[ $dbids_to_orders[ $menu_item_id ] - 1 ] )
  198. || $orders_to_dbids[ $dbids_to_orders[ $menu_item_id ] - 1 ] !== (int) $menu_item_data['menu_item_parent']
  199. ) {
  200. // Just make it a child of the previous; keep the order.
  201. $menu_item_data['menu_item_parent'] = (int) $orders_to_dbids[ $dbids_to_orders[ $menu_item_id ] - 1 ];
  202. update_post_meta( $menu_item_data['ID'], '_menu_item_menu_item_parent', (int) $menu_item_data['menu_item_parent'] );
  203. wp_update_post( $menu_item_data );
  204. }
  205. }
  206. }
  207. }
  208. break;
  209. case 'delete-menu-item':
  210. $menu_item_id = (int) $_REQUEST['menu-item'];
  211. check_admin_referer( 'delete-menu_item_' . $menu_item_id );
  212. if ( is_nav_menu_item( $menu_item_id ) && wp_delete_post( $menu_item_id, true ) ) {
  213. $messages[] = '<div id="message" class="updated notice is-dismissible"><p>' . __( 'The menu item has been successfully deleted.' ) . '</p></div>';
  214. }
  215. break;
  216. case 'delete':
  217. check_admin_referer( 'delete-nav_menu-' . $nav_menu_selected_id );
  218. if ( is_nav_menu( $nav_menu_selected_id ) ) {
  219. $deletion = wp_delete_nav_menu( $nav_menu_selected_id );
  220. } else {
  221. // Reset the selected menu.
  222. $nav_menu_selected_id = 0;
  223. unset( $_REQUEST['menu'] );
  224. }
  225. if ( ! isset( $deletion ) ) {
  226. break;
  227. }
  228. if ( is_wp_error( $deletion ) ) {
  229. $messages[] = '<div id="message" class="error notice is-dismissible"><p>' . $deletion->get_error_message() . '</p></div>';
  230. } else {
  231. $messages[] = '<div id="message" class="updated notice is-dismissible"><p>' . __( 'The menu has been successfully deleted.' ) . '</p></div>';
  232. }
  233. break;
  234. case 'delete_menus':
  235. check_admin_referer( 'nav_menus_bulk_actions' );
  236. foreach ( $_REQUEST['delete_menus'] as $menu_id_to_delete ) {
  237. if ( ! is_nav_menu( $menu_id_to_delete ) ) {
  238. continue;
  239. }
  240. $deletion = wp_delete_nav_menu( $menu_id_to_delete );
  241. if ( is_wp_error( $deletion ) ) {
  242. $messages[] = '<div id="message" class="error notice is-dismissible"><p>' . $deletion->get_error_message() . '</p></div>';
  243. $deletion_error = true;
  244. }
  245. }
  246. if ( empty( $deletion_error ) ) {
  247. $messages[] = '<div id="message" class="updated notice is-dismissible"><p>' . __( 'Selected menus have been successfully deleted.' ) . '</p></div>';
  248. }
  249. break;
  250. case 'update':
  251. check_admin_referer( 'update-nav_menu', 'update-nav-menu-nonce' );
  252. // Merge new and existing menu locations if any new ones are set.
  253. $new_menu_locations = array();
  254. if ( isset( $_POST['menu-locations'] ) ) {
  255. $new_menu_locations = array_map( 'absint', $_POST['menu-locations'] );
  256. $menu_locations = array_merge( $menu_locations, $new_menu_locations );
  257. }
  258. // Add Menu.
  259. if ( 0 === $nav_menu_selected_id ) {
  260. $new_menu_title = trim( esc_html( $_POST['menu-name'] ) );
  261. if ( $new_menu_title ) {
  262. $_nav_menu_selected_id = wp_update_nav_menu_object( 0, array( 'menu-name' => $new_menu_title ) );
  263. if ( is_wp_error( $_nav_menu_selected_id ) ) {
  264. $messages[] = '<div id="message" class="error notice is-dismissible"><p>' . $_nav_menu_selected_id->get_error_message() . '</p></div>';
  265. } else {
  266. $_menu_object = wp_get_nav_menu_object( $_nav_menu_selected_id );
  267. $nav_menu_selected_id = $_nav_menu_selected_id;
  268. $nav_menu_selected_title = $_menu_object->name;
  269. if ( isset( $_REQUEST['menu-item'] ) ) {
  270. wp_save_nav_menu_items( $nav_menu_selected_id, absint( $_REQUEST['menu-item'] ) );
  271. }
  272. if ( isset( $_REQUEST['zero-menu-state'] ) || ! empty( $_POST['auto-add-pages'] ) ) {
  273. // If there are menu items, add them.
  274. wp_nav_menu_update_menu_items( $nav_menu_selected_id, $nav_menu_selected_title );
  275. }
  276. if ( isset( $_REQUEST['zero-menu-state'] ) ) {
  277. // Auto-save nav_menu_locations.
  278. $locations = get_nav_menu_locations();
  279. foreach ( $locations as $location => $menu_id ) {
  280. $locations[ $location ] = $nav_menu_selected_id;
  281. break; // There should only be 1.
  282. }
  283. set_theme_mod( 'nav_menu_locations', $locations );
  284. } elseif ( count( $new_menu_locations ) > 0 ) {
  285. // If locations have been selected for the new menu, save those.
  286. $locations = get_nav_menu_locations();
  287. foreach ( array_keys( $new_menu_locations ) as $location ) {
  288. $locations[ $location ] = $nav_menu_selected_id;
  289. }
  290. set_theme_mod( 'nav_menu_locations', $locations );
  291. }
  292. if ( isset( $_REQUEST['use-location'] ) ) {
  293. $locations = get_registered_nav_menus();
  294. $menu_locations = get_nav_menu_locations();
  295. if ( isset( $locations[ $_REQUEST['use-location'] ] ) ) {
  296. $menu_locations[ $_REQUEST['use-location'] ] = $nav_menu_selected_id;
  297. }
  298. set_theme_mod( 'nav_menu_locations', $menu_locations );
  299. }
  300. wp_redirect( admin_url( 'nav-menus.php?menu=' . $_nav_menu_selected_id ) );
  301. exit;
  302. }
  303. } else {
  304. $messages[] = '<div id="message" class="error notice is-dismissible"><p>' . __( 'Please enter a valid menu name.' ) . '</p></div>';
  305. }
  306. // Update existing menu.
  307. } else {
  308. // Remove menu locations that have been unchecked.
  309. foreach ( $locations as $location => $description ) {
  310. if ( ( empty( $_POST['menu-locations'] ) || empty( $_POST['menu-locations'][ $location ] ) )
  311. && isset( $menu_locations[ $location ] ) && $menu_locations[ $location ] === $nav_menu_selected_id
  312. ) {
  313. unset( $menu_locations[ $location ] );
  314. }
  315. }
  316. // Set menu locations.
  317. set_theme_mod( 'nav_menu_locations', $menu_locations );
  318. $_menu_object = wp_get_nav_menu_object( $nav_menu_selected_id );
  319. $menu_title = trim( esc_html( $_POST['menu-name'] ) );
  320. if ( ! $menu_title ) {
  321. $messages[] = '<div id="message" class="error notice is-dismissible"><p>' . __( 'Please enter a valid menu name.' ) . '</p></div>';
  322. $menu_title = $_menu_object->name;
  323. }
  324. if ( ! is_wp_error( $_menu_object ) ) {
  325. $_nav_menu_selected_id = wp_update_nav_menu_object( $nav_menu_selected_id, array( 'menu-name' => $menu_title ) );
  326. if ( is_wp_error( $_nav_menu_selected_id ) ) {
  327. $_menu_object = $_nav_menu_selected_id;
  328. $messages[] = '<div id="message" class="error notice is-dismissible"><p>' . $_nav_menu_selected_id->get_error_message() . '</p></div>';
  329. } else {
  330. $_menu_object = wp_get_nav_menu_object( $_nav_menu_selected_id );
  331. $nav_menu_selected_title = $_menu_object->name;
  332. }
  333. }
  334. // Update menu items.
  335. if ( ! is_wp_error( $_menu_object ) ) {
  336. $messages = array_merge( $messages, wp_nav_menu_update_menu_items( $_nav_menu_selected_id, $nav_menu_selected_title ) );
  337. // If the menu ID changed, redirect to the new URL.
  338. if ( $nav_menu_selected_id !== $_nav_menu_selected_id ) {
  339. wp_redirect( admin_url( 'nav-menus.php?menu=' . (int) $_nav_menu_selected_id ) );
  340. exit;
  341. }
  342. }
  343. }
  344. break;
  345. case 'locations':
  346. if ( ! $num_locations ) {
  347. wp_redirect( admin_url( 'nav-menus.php' ) );
  348. exit;
  349. }
  350. add_filter( 'screen_options_show_screen', '__return_false' );
  351. if ( isset( $_POST['menu-locations'] ) ) {
  352. check_admin_referer( 'save-menu-locations' );
  353. $new_menu_locations = array_map( 'absint', $_POST['menu-locations'] );
  354. $menu_locations = array_merge( $menu_locations, $new_menu_locations );
  355. // Set menu locations.
  356. set_theme_mod( 'nav_menu_locations', $menu_locations );
  357. $messages[] = '<div id="message" class="updated notice is-dismissible"><p>' . __( 'Menu locations updated.' ) . '</p></div>';
  358. }
  359. break;
  360. }
  361. // Get all nav menus.
  362. $nav_menus = wp_get_nav_menus();
  363. $menu_count = count( $nav_menus );
  364. // Are we on the add new screen?
  365. $add_new_screen = ( isset( $_GET['menu'] ) && 0 === (int) $_GET['menu'] ) ? true : false;
  366. $locations_screen = ( isset( $_GET['action'] ) && 'locations' === $_GET['action'] ) ? true : false;
  367. $page_count = wp_count_posts( 'page' );
  368. /*
  369. * If we have one theme location, and zero menus, we take them right
  370. * into editing their first menu.
  371. */
  372. if ( 1 === count( get_registered_nav_menus() ) && ! $add_new_screen
  373. && empty( $nav_menus ) && ! empty( $page_count->publish )
  374. ) {
  375. $one_theme_location_no_menus = true;
  376. } else {
  377. $one_theme_location_no_menus = false;
  378. }
  379. $nav_menus_l10n = array(
  380. 'oneThemeLocationNoMenus' => $one_theme_location_no_menus,
  381. 'moveUp' => __( 'Move up one' ),
  382. 'moveDown' => __( 'Move down one' ),
  383. 'moveToTop' => __( 'Move to the top' ),
  384. /* translators: %s: Previous item name. */
  385. 'moveUnder' => __( 'Move under %s' ),
  386. /* translators: %s: Previous item name. */
  387. 'moveOutFrom' => __( 'Move out from under %s' ),
  388. /* translators: %s: Previous item name. */
  389. 'under' => __( 'Under %s' ),
  390. /* translators: %s: Previous item name. */
  391. 'outFrom' => __( 'Out from under %s' ),
  392. /* translators: 1: Item name, 2: Item position, 3: Total number of items. */
  393. 'menuFocus' => __( '%1$s. Menu item %2$d of %3$d.' ),
  394. /* translators: 1: Item name, 2: Item position, 3: Parent item name. */
  395. 'subMenuFocus' => __( '%1$s. Sub item number %2$d under %3$s.' ),
  396. /* translators: %s: Item name. */
  397. 'menuItemDeletion' => __( 'item %s' ),
  398. /* translators: %s: Item name. */
  399. 'itemsDeleted' => __( 'Deleted menu item: %s.' ),
  400. 'itemAdded' => __( 'Menu item added' ),
  401. 'itemRemoved' => __( 'Menu item removed' ),
  402. 'movedUp' => __( 'Menu item moved up' ),
  403. 'movedDown' => __( 'Menu item moved down' ),
  404. 'movedTop' => __( 'Menu item moved to the top' ),
  405. 'movedLeft' => __( 'Menu item moved out of submenu' ),
  406. 'movedRight' => __( 'Menu item is now a sub-item' ),
  407. );
  408. wp_localize_script( 'nav-menu', 'menus', $nav_menus_l10n );
  409. /*
  410. * Redirect to add screen if there are no menus and this users has either zero,
  411. * or more than 1 theme locations.
  412. */
  413. if ( 0 === $menu_count && ! $add_new_screen && ! $one_theme_location_no_menus ) {
  414. wp_redirect( admin_url( 'nav-menus.php?action=edit&menu=0' ) );
  415. }
  416. // Get recently edited nav menu.
  417. $recently_edited = absint( get_user_option( 'nav_menu_recently_edited' ) );
  418. if ( empty( $recently_edited ) && is_nav_menu( $nav_menu_selected_id ) ) {
  419. $recently_edited = $nav_menu_selected_id;
  420. }
  421. // Use $recently_edited if none are selected.
  422. if ( empty( $nav_menu_selected_id ) && ! isset( $_GET['menu'] ) && is_nav_menu( $recently_edited ) ) {
  423. $nav_menu_selected_id = $recently_edited;
  424. }
  425. // On deletion of menu, if another menu exists, show it.
  426. if ( ! $add_new_screen && $menu_count > 0 && isset( $_GET['action'] ) && 'delete' === $_GET['action'] ) {
  427. $nav_menu_selected_id = $nav_menus[0]->term_id;
  428. }
  429. // Set $nav_menu_selected_id to 0 if no menus.
  430. if ( $one_theme_location_no_menus ) {
  431. $nav_menu_selected_id = 0;
  432. } elseif ( empty( $nav_menu_selected_id ) && ! empty( $nav_menus ) && ! $add_new_screen ) {
  433. // If we have no selection yet, and we have menus, set to the first one in the list.
  434. $nav_menu_selected_id = $nav_menus[0]->term_id;
  435. }
  436. // Update the user's setting.
  437. if ( $nav_menu_selected_id !== $recently_edited && is_nav_menu( $nav_menu_selected_id ) ) {
  438. update_user_meta( $current_user->ID, 'nav_menu_recently_edited', $nav_menu_selected_id );
  439. }
  440. // If there's a menu, get its name.
  441. if ( ! $nav_menu_selected_title && is_nav_menu( $nav_menu_selected_id ) ) {
  442. $_menu_object = wp_get_nav_menu_object( $nav_menu_selected_id );
  443. $nav_menu_selected_title = ! is_wp_error( $_menu_object ) ? $_menu_object->name : '';
  444. }
  445. // Generate truncated menu names.
  446. foreach ( (array) $nav_menus as $key => $_nav_menu ) {
  447. $nav_menus[ $key ]->truncated_name = wp_html_excerpt( $_nav_menu->name, 40, '&hellip;' );
  448. }
  449. // Retrieve menu locations.
  450. if ( current_theme_supports( 'menus' ) ) {
  451. $locations = get_registered_nav_menus();
  452. $menu_locations = get_nav_menu_locations();
  453. }
  454. /*
  455. * Ensure the user will be able to scroll horizontally
  456. * by adding a class for the max menu depth.
  457. *
  458. * @global int $_wp_nav_menu_max_depth
  459. */
  460. global $_wp_nav_menu_max_depth;
  461. $_wp_nav_menu_max_depth = 0;
  462. // Calling wp_get_nav_menu_to_edit generates $_wp_nav_menu_max_depth.
  463. if ( is_nav_menu( $nav_menu_selected_id ) ) {
  464. $menu_items = wp_get_nav_menu_items( $nav_menu_selected_id, array( 'post_status' => 'any' ) );
  465. $edit_markup = wp_get_nav_menu_to_edit( $nav_menu_selected_id );
  466. }
  467. /**
  468. * @global int $_wp_nav_menu_max_depth
  469. *
  470. * @param string $classes
  471. * @return string
  472. */
  473. function wp_nav_menu_max_depth( $classes ) {
  474. global $_wp_nav_menu_max_depth;
  475. return "$classes menu-max-depth-$_wp_nav_menu_max_depth";
  476. }
  477. add_filter( 'admin_body_class', 'wp_nav_menu_max_depth' );
  478. wp_nav_menu_setup();
  479. wp_initial_nav_menu_meta_boxes();
  480. if ( ! current_theme_supports( 'menus' ) && ! $num_locations ) {
  481. $messages[] = '<div id="message" class="updated"><p>' . sprintf(
  482. /* translators: %s: URL to Widgets screen. */
  483. __( 'Your theme does not natively support menus, but you can use them in sidebars by adding a &#8220;Navigation Menu&#8221; widget on the <a href="%s">Widgets</a> screen.' ),
  484. admin_url( 'widgets.php' )
  485. ) . '</p></div>';
  486. }
  487. if ( ! $locations_screen ) : // Main tab.
  488. $overview = '<p>' . __( 'This screen is used for managing your navigation menus.' ) . '</p>';
  489. $overview .= '<p>' . sprintf(
  490. /* translators: 1: URL to Widgets screen, 2 and 3: The names of the default themes. */
  491. __( 'Menus can be displayed in locations defined by your theme, even used in sidebars by adding a &#8220;Navigation Menu&#8221; widget on the <a href="%1$s">Widgets</a> screen. If your theme does not support the navigation menus feature (the default themes, %2$s and %3$s, do), you can learn about adding this support by following the documentation link to the side.' ),
  492. admin_url( 'widgets.php' ),
  493. 'Twenty Twenty',
  494. 'Twenty Twenty-One'
  495. ) . '</p>';
  496. $overview .= '<p>' . __( 'From this screen you can:' ) . '</p>';
  497. $overview .= '<ul><li>' . __( 'Create, edit, and delete menus' ) . '</li>';
  498. $overview .= '<li>' . __( 'Add, organize, and modify individual menu items' ) . '</li></ul>';
  499. get_current_screen()->add_help_tab(
  500. array(
  501. 'id' => 'overview',
  502. 'title' => __( 'Overview' ),
  503. 'content' => $overview,
  504. )
  505. );
  506. $menu_management = '<p>' . __( 'The menu management box at the top of the screen is used to control which menu is opened in the editor below.' ) . '</p>';
  507. $menu_management .= '<ul><li>' . __( 'To edit an existing menu, <strong>choose a menu from the drop down and click Select</strong>' ) . '</li>';
  508. $menu_management .= '<li>' . __( 'If you have not yet created any menus, <strong>click the &#8217;create a new menu&#8217; link</strong> to get started' ) . '</li></ul>';
  509. $menu_management .= '<p>' . __( 'You can assign theme locations to individual menus by <strong>selecting the desired settings</strong> at the bottom of the menu editor. To assign menus to all theme locations at once, <strong>visit the Manage Locations tab</strong> at the top of the screen.' ) . '</p>';
  510. get_current_screen()->add_help_tab(
  511. array(
  512. 'id' => 'menu-management',
  513. 'title' => __( 'Menu Management' ),
  514. 'content' => $menu_management,
  515. )
  516. );
  517. $editing_menus = '<p>' . __( 'Each navigation menu may contain a mix of links to pages, categories, custom URLs or other content types. Menu links are added by selecting items from the expanding boxes in the left-hand column below.' ) . '</p>';
  518. $editing_menus .= '<p>' . __( '<strong>Clicking the arrow to the right of any menu item</strong> in the editor will reveal a standard group of settings. Additional settings such as link target, CSS classes, link relationships, and link descriptions can be enabled and disabled via the Screen Options tab.' ) . '</p>';
  519. $editing_menus .= '<ul><li>' . __( 'Add one or several items at once by <strong>selecting the checkbox next to each item and clicking Add to Menu</strong>' ) . '</li>';
  520. $editing_menus .= '<li>' . __( 'To add a custom link, <strong>expand the Custom Links section, enter a URL and link text, and click Add to Menu</strong>' ) . '</li>';
  521. $editing_menus .= '<li>' . __( 'To reorganize menu items, <strong>drag and drop items with your mouse or use your keyboard</strong>. Drag or move a menu item a little to the right to make it a submenu' ) . '</li>';
  522. $editing_menus .= '<li>' . __( 'Delete a menu item by <strong>expanding it and clicking the Remove link</strong>' ) . '</li></ul>';
  523. get_current_screen()->add_help_tab(
  524. array(
  525. 'id' => 'editing-menus',
  526. 'title' => __( 'Editing Menus' ),
  527. 'content' => $editing_menus,
  528. )
  529. );
  530. else : // Locations tab.
  531. $locations_overview = '<p>' . __( 'This screen is used for globally assigning menus to locations defined by your theme.' ) . '</p>';
  532. $locations_overview .= '<ul><li>' . __( 'To assign menus to one or more theme locations, <strong>select a menu from each location&#8217;s drop down.</strong> When you are finished, <strong>click Save Changes</strong>' ) . '</li>';
  533. $locations_overview .= '<li>' . __( 'To edit a menu currently assigned to a theme location, <strong>click the adjacent &#8217;Edit&#8217; link</strong>' ) . '</li>';
  534. $locations_overview .= '<li>' . __( 'To add a new menu instead of assigning an existing one, <strong>click the &#8217;Use new menu&#8217; link</strong>. Your new menu will be automatically assigned to that theme location' ) . '</li></ul>';
  535. get_current_screen()->add_help_tab(
  536. array(
  537. 'id' => 'locations-overview',
  538. 'title' => __( 'Overview' ),
  539. 'content' => $locations_overview,
  540. )
  541. );
  542. endif;
  543. get_current_screen()->set_help_sidebar(
  544. '<p><strong>' . __( 'For more information:' ) . '</strong></p>' .
  545. '<p>' . __( '<a href="https://wordpress.org/support/article/appearance-menus-screen/">Documentation on Menus</a>' ) . '</p>' .
  546. '<p>' . __( '<a href="https://wordpress.org/support/">Support</a>' ) . '</p>'
  547. );
  548. // Get the admin header.
  549. require_once ABSPATH . 'wp-admin/admin-header.php';
  550. ?>
  551. <div class="wrap">
  552. <h1 class="wp-heading-inline"><?php esc_html_e( 'Menus' ); ?></h1>
  553. <?php
  554. if ( current_user_can( 'customize' ) ) :
  555. $focus = $locations_screen ? array( 'section' => 'menu_locations' ) : array( 'panel' => 'nav_menus' );
  556. printf(
  557. ' <a class="page-title-action hide-if-no-customize" href="%1$s">%2$s</a>',
  558. esc_url(
  559. add_query_arg(
  560. array(
  561. array( 'autofocus' => $focus ),
  562. 'return' => urlencode( remove_query_arg( wp_removable_query_args(), wp_unslash( $_SERVER['REQUEST_URI'] ) ) ),
  563. ),
  564. admin_url( 'customize.php' )
  565. )
  566. ),
  567. __( 'Manage with Live Preview' )
  568. );
  569. endif;
  570. $nav_tab_active_class = '';
  571. $nav_aria_current = '';
  572. if ( ! isset( $_GET['action'] ) || isset( $_GET['action'] ) && 'locations' !== $_GET['action'] ) {
  573. $nav_tab_active_class = ' nav-tab-active';
  574. $nav_aria_current = ' aria-current="page"';
  575. }
  576. ?>
  577. <hr class="wp-header-end">
  578. <nav class="nav-tab-wrapper wp-clearfix" aria-label="<?php esc_attr_e( 'Secondary menu' ); ?>">
  579. <a href="<?php echo esc_url( admin_url( 'nav-menus.php' ) ); ?>" class="nav-tab<?php echo $nav_tab_active_class; ?>"<?php echo $nav_aria_current; ?>><?php esc_html_e( 'Edit Menus' ); ?></a>
  580. <?php
  581. if ( $num_locations && $menu_count ) {
  582. $active_tab_class = '';
  583. $aria_current = '';
  584. if ( $locations_screen ) {
  585. $active_tab_class = ' nav-tab-active';
  586. $aria_current = ' aria-current="page"';
  587. }
  588. ?>
  589. <a href="<?php echo esc_url( add_query_arg( array( 'action' => 'locations' ), admin_url( 'nav-menus.php' ) ) ); ?>" class="nav-tab<?php echo $active_tab_class; ?>"<?php echo $aria_current; ?>><?php esc_html_e( 'Manage Locations' ); ?></a>
  590. <?php
  591. }
  592. ?>
  593. </nav>
  594. <?php
  595. foreach ( $messages as $message ) :
  596. echo $message . "\n";
  597. endforeach;
  598. ?>
  599. <?php
  600. if ( $locations_screen ) :
  601. if ( 1 === $num_locations ) {
  602. echo '<p>' . __( 'Your theme supports one menu. Select which menu you would like to use.' ) . '</p>';
  603. } else {
  604. echo '<p>' . sprintf(
  605. /* translators: %s: Number of menus. */
  606. _n(
  607. 'Your theme supports %s menu. Select which menu appears in each location.',
  608. 'Your theme supports %s menus. Select which menu appears in each location.',
  609. $num_locations
  610. ),
  611. number_format_i18n( $num_locations )
  612. ) . '</p>';
  613. }
  614. ?>
  615. <div id="menu-locations-wrap">
  616. <form method="post" action="<?php echo esc_url( add_query_arg( array( 'action' => 'locations' ), admin_url( 'nav-menus.php' ) ) ); ?>">
  617. <table class="widefat fixed" id="menu-locations-table">
  618. <thead>
  619. <tr>
  620. <th scope="col" class="manage-column column-locations"><?php _e( 'Theme Location' ); ?></th>
  621. <th scope="col" class="manage-column column-menus"><?php _e( 'Assigned Menu' ); ?></th>
  622. </tr>
  623. </thead>
  624. <tbody class="menu-locations">
  625. <?php foreach ( $locations as $_location => $_name ) { ?>
  626. <tr class="menu-locations-row">
  627. <td class="menu-location-title"><label for="locations-<?php echo $_location; ?>"><?php echo $_name; ?></label></td>
  628. <td class="menu-location-menus">
  629. <select name="menu-locations[<?php echo $_location; ?>]" id="locations-<?php echo $_location; ?>">
  630. <option value="0"><?php printf( '&mdash; %s &mdash;', esc_html__( 'Select a Menu' ) ); ?></option>
  631. <?php
  632. foreach ( $nav_menus as $menu ) :
  633. $data_orig = '';
  634. $selected = isset( $menu_locations[ $_location ] ) && $menu_locations[ $_location ] === $menu->term_id;
  635. if ( $selected ) {
  636. $data_orig = 'data-orig="true"';
  637. }
  638. ?>
  639. <option <?php echo $data_orig; ?> <?php selected( $selected ); ?> value="<?php echo $menu->term_id; ?>">
  640. <?php echo wp_html_excerpt( $menu->name, 40, '&hellip;' ); ?>
  641. </option>
  642. <?php endforeach; ?>
  643. </select>
  644. <div class="locations-row-links">
  645. <?php if ( isset( $menu_locations[ $_location ] ) && 0 !== $menu_locations[ $_location ] ) : ?>
  646. <span class="locations-edit-menu-link">
  647. <a href="
  648. <?php
  649. echo esc_url(
  650. add_query_arg(
  651. array(
  652. 'action' => 'edit',
  653. 'menu' => $menu_locations[ $_location ],
  654. ),
  655. admin_url( 'nav-menus.php' )
  656. )
  657. );
  658. ?>
  659. ">
  660. <span aria-hidden="true"><?php _ex( 'Edit', 'menu' ); ?></span><span class="screen-reader-text"><?php _e( 'Edit selected menu' ); ?></span>
  661. </a>
  662. </span>
  663. <?php endif; ?>
  664. <span class="locations-add-menu-link">
  665. <a href="
  666. <?php
  667. echo esc_url(
  668. add_query_arg(
  669. array(
  670. 'action' => 'edit',
  671. 'menu' => 0,
  672. 'use-location' => $_location,
  673. ),
  674. admin_url( 'nav-menus.php' )
  675. )
  676. );
  677. ?>
  678. ">
  679. <?php _ex( 'Use new menu', 'menu' ); ?>
  680. </a>
  681. </span>
  682. </div><!-- .locations-row-links -->
  683. </td><!-- .menu-location-menus -->
  684. </tr><!-- .menu-locations-row -->
  685. <?php } // End foreach. ?>
  686. </tbody>
  687. </table>
  688. <p class="button-controls wp-clearfix"><?php submit_button( __( 'Save Changes' ), 'primary left', 'nav-menu-locations', false ); ?></p>
  689. <?php wp_nonce_field( 'save-menu-locations' ); ?>
  690. <input type="hidden" name="menu" id="nav-menu-meta-object-id" value="<?php echo esc_attr( $nav_menu_selected_id ); ?>" />
  691. </form>
  692. </div><!-- #menu-locations-wrap -->
  693. <?php
  694. /**
  695. * Fires after the menu locations table is displayed.
  696. *
  697. * @since 3.6.0
  698. */
  699. do_action( 'after_menu_locations_table' );
  700. ?>
  701. <?php else : ?>
  702. <div class="manage-menus">
  703. <?php if ( $menu_count < 1 ) : ?>
  704. <span class="first-menu-message">
  705. <?php _e( 'Create your first menu below.' ); ?>
  706. <span class="screen-reader-text"><?php _e( 'Fill in the Menu Name and click the Create Menu button to create your first menu.' ); ?></span>
  707. </span><!-- /first-menu-message -->
  708. <?php elseif ( $menu_count < 2 ) : ?>
  709. <span class="add-edit-menu-action">
  710. <?php
  711. printf(
  712. /* translators: %s: URL to create a new menu. */
  713. __( 'Edit your menu below, or <a href="%s">create a new menu</a>. Do not forget to save your changes!' ),
  714. esc_url(
  715. add_query_arg(
  716. array(
  717. 'action' => 'edit',
  718. 'menu' => 0,
  719. ),
  720. admin_url( 'nav-menus.php' )
  721. )
  722. )
  723. );
  724. ?>
  725. <span class="screen-reader-text"><?php _e( 'Click the Save Menu button to save your changes.' ); ?></span>
  726. </span><!-- /add-edit-menu-action -->
  727. <?php else : ?>
  728. <form method="get" action="<?php echo esc_url( admin_url( 'nav-menus.php' ) ); ?>">
  729. <input type="hidden" name="action" value="edit" />
  730. <label for="select-menu-to-edit" class="selected-menu"><?php _e( 'Select a menu to edit:' ); ?></label>
  731. <select name="menu" id="select-menu-to-edit">
  732. <?php if ( $add_new_screen ) : ?>
  733. <option value="0" selected="selected"><?php _e( '&mdash; Select &mdash;' ); ?></option>
  734. <?php endif; ?>
  735. <?php foreach ( (array) $nav_menus as $_nav_menu ) : ?>
  736. <option value="<?php echo esc_attr( $_nav_menu->term_id ); ?>" <?php selected( $_nav_menu->term_id, $nav_menu_selected_id ); ?>>
  737. <?php
  738. echo esc_html( $_nav_menu->truncated_name );
  739. if ( ! empty( $menu_locations ) && in_array( $_nav_menu->term_id, $menu_locations, true ) ) {
  740. $locations_assigned_to_this_menu = array();
  741. foreach ( array_keys( $menu_locations, $_nav_menu->term_id, true ) as $menu_location_key ) {
  742. if ( isset( $locations[ $menu_location_key ] ) ) {
  743. $locations_assigned_to_this_menu[] = $locations[ $menu_location_key ];
  744. }
  745. }
  746. /**
  747. * Filters the number of locations listed per menu in the drop-down select.
  748. *
  749. * @since 3.6.0
  750. *
  751. * @param int $locations Number of menu locations to list. Default 3.
  752. */
  753. $locations_listed_per_menu = absint( apply_filters( 'wp_nav_locations_listed_per_menu', 3 ) );
  754. $assigned_locations = array_slice( $locations_assigned_to_this_menu, 0, $locations_listed_per_menu );
  755. // Adds ellipses following the number of locations defined in $assigned_locations.
  756. if ( ! empty( $assigned_locations ) ) {
  757. printf(
  758. ' (%1$s%2$s)',
  759. implode( ', ', $assigned_locations ),
  760. count( $locations_assigned_to_this_menu ) > count( $assigned_locations ) ? ' &hellip;' : ''
  761. );
  762. }
  763. }
  764. ?>
  765. </option>
  766. <?php endforeach; ?>
  767. </select>
  768. <span class="submit-btn"><input type="submit" class="button" value="<?php esc_attr_e( 'Select' ); ?>"></span>
  769. <span class="add-new-menu-action">
  770. <?php
  771. printf(
  772. /* translators: %s: URL to create a new menu. */
  773. __( 'or <a href="%s">create a new menu</a>. Do not forget to save your changes!' ),
  774. esc_url(
  775. add_query_arg(
  776. array(
  777. 'action' => 'edit',
  778. 'menu' => 0,
  779. ),
  780. admin_url( 'nav-menus.php' )
  781. )
  782. )
  783. );
  784. ?>
  785. <span class="screen-reader-text"><?php _e( 'Click the Save Menu button to save your changes.' ); ?></span>
  786. </span><!-- /add-new-menu-action -->
  787. </form>
  788. <?php
  789. endif;
  790. $metabox_holder_disabled_class = '';
  791. if ( isset( $_GET['menu'] ) && 0 === (int) $_GET['menu'] ) {
  792. $metabox_holder_disabled_class = ' metabox-holder-disabled';
  793. }
  794. ?>
  795. </div><!-- /manage-menus -->
  796. <div id="nav-menus-frame" class="wp-clearfix">
  797. <div id="menu-settings-column" class="metabox-holder<?php echo $metabox_holder_disabled_class; ?>">
  798. <div class="clear"></div>
  799. <form id="nav-menu-meta" class="nav-menu-meta" method="post" enctype="multipart/form-data">
  800. <input type="hidden" name="menu" id="nav-menu-meta-object-id" value="<?php echo esc_attr( $nav_menu_selected_id ); ?>" />
  801. <input type="hidden" name="action" value="add-menu-item" />
  802. <?php wp_nonce_field( 'add-menu_item', 'menu-settings-column-nonce' ); ?>
  803. <h2><?php _e( 'Add menu items' ); ?></h2>
  804. <?php do_accordion_sections( 'nav-menus', 'side', null ); ?>
  805. </form>
  806. </div><!-- /#menu-settings-column -->
  807. <div id="menu-management-liquid">
  808. <div id="menu-management">
  809. <form id="update-nav-menu" method="post" enctype="multipart/form-data">
  810. <h2><?php _e( 'Menu structure' ); ?></h2>
  811. <div class="menu-edit">
  812. <input type="hidden" name="nav-menu-data">
  813. <?php
  814. wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false );
  815. wp_nonce_field( 'meta-box-order', 'meta-box-order-nonce', false );
  816. wp_nonce_field( 'update-nav_menu', 'update-nav-menu-nonce' );
  817. $menu_name_aria_desc = $add_new_screen ? ' aria-describedby="menu-name-desc"' : '';
  818. if ( $one_theme_location_no_menus ) {
  819. $menu_name_val = 'value="' . esc_attr( 'Menu 1' ) . '"';
  820. ?>
  821. <input type="hidden" name="zero-menu-state" value="true" />
  822. <?php
  823. } else {
  824. $menu_name_val = 'value="' . esc_attr( $nav_menu_selected_title ) . '"';
  825. }
  826. ?>
  827. <input type="hidden" name="action" value="update" />
  828. <input type="hidden" name="menu" id="menu" value="<?php echo esc_attr( $nav_menu_selected_id ); ?>" />
  829. <div id="nav-menu-header">
  830. <div class="major-publishing-actions wp-clearfix">
  831. <label class="menu-name-label" for="menu-name"><?php _e( 'Menu Name' ); ?></label>
  832. <input name="menu-name" id="menu-name" type="text" class="menu-name regular-text menu-item-textbox form-required" required="required" <?php echo $menu_name_val . $menu_name_aria_desc; ?> />
  833. <div class="publishing-action">
  834. <?php submit_button( empty( $nav_menu_selected_id ) ? __( 'Create Menu' ) : __( 'Save Menu' ), 'primary large menu-save', 'save_menu', false, array( 'id' => 'save_menu_header' ) ); ?>
  835. </div><!-- END .publishing-action -->
  836. </div><!-- END .major-publishing-actions -->
  837. </div><!-- END .nav-menu-header -->
  838. <div id="post-body">
  839. <div id="post-body-content" class="wp-clearfix">
  840. <?php if ( ! $add_new_screen ) : ?>
  841. <?php
  842. $hide_style = '';
  843. if ( isset( $menu_items ) && 0 === count( $menu_items ) ) {
  844. $hide_style = 'style="display: none;"';
  845. }
  846. if ( $one_theme_location_no_menus ) {
  847. $starter_copy = __( 'Edit your default menu by adding or removing items. Drag the items into the order you prefer. Click Create Menu to save your changes.' );
  848. } else {
  849. $starter_copy = __( 'Drag the items into the order you prefer. Click the arrow on the right of the item to reveal additional configuration options.' );
  850. }
  851. ?>
  852. <div class="drag-instructions post-body-plain" <?php echo $hide_style; ?>>
  853. <p><?php echo $starter_copy; ?></p>
  854. </div>
  855. <?php if ( ! $add_new_screen ) : ?>
  856. <div id="nav-menu-bulk-actions-top" class="bulk-actions">
  857. <label class="bulk-select-button" for="bulk-select-switcher-top">
  858. <input type="checkbox" id="bulk-select-switcher-top" name="bulk-select-switcher-top" class="bulk-select-switcher">
  859. <span class="bulk-select-button-label"><?php _e( 'Bulk Select' ); ?></span>
  860. </label>
  861. </div>
  862. <?php endif; ?>
  863. <?php
  864. if ( isset( $edit_markup ) && ! is_wp_error( $edit_markup ) ) {
  865. echo $edit_markup;
  866. } else {
  867. ?>
  868. <ul class="menu" id="menu-to-edit"></ul>
  869. <?php } ?>
  870. <?php endif; ?>
  871. <?php if ( $add_new_screen ) : ?>
  872. <p class="post-body-plain" id="menu-name-desc"><?php _e( 'Give your menu a name, then click Create Menu.' ); ?></p>
  873. <?php if ( isset( $_GET['use-location'] ) ) : ?>
  874. <input type="hidden" name="use-location" value="<?php echo esc_attr( $_GET['use-location'] ); ?>" />
  875. <?php endif; ?>
  876. <?php
  877. endif;
  878. $no_menus_style = '';
  879. if ( $one_theme_location_no_menus ) {
  880. $no_menus_style = 'style="display: none;"';
  881. }
  882. ?>
  883. <?php if ( ! $add_new_screen ) : ?>
  884. <div id="nav-menu-bulk-actions-bottom" class="bulk-actions">
  885. <label class="bulk-select-button" for="bulk-select-switcher-bottom">
  886. <input type="checkbox" id="bulk-select-switcher-bottom" name="bulk-select-switcher-top" class="bulk-select-switcher">
  887. <span class="bulk-select-button-label"><?php _e( 'Bulk Select' ); ?></span>
  888. </label>
  889. <input type="button" class="deletion menu-items-delete disabled" value="<?php _e( 'Remove Selected Items' ); ?>">
  890. <div id="pending-menu-items-to-delete">
  891. <p><?php _e( 'List of menu items selected for deletion:' ); ?></p>
  892. <ul></ul>
  893. </div>
  894. </div>
  895. <?php endif; ?>
  896. <div class="menu-settings" <?php echo $no_menus_style; ?>>
  897. <h3><?php _e( 'Menu Settings' ); ?></h3>
  898. <?php
  899. if ( ! isset( $auto_add ) ) {
  900. $auto_add = get_option( 'nav_menu_options' );
  901. if ( ! isset( $auto_add['auto_add'] ) ) {
  902. $auto_add = false;
  903. } elseif ( false !== array_search( $nav_menu_selected_id, $auto_add['auto_add'], true ) ) {
  904. $auto_add = true;
  905. } else {
  906. $auto_add = false;
  907. }
  908. }
  909. ?>
  910. <fieldset class="menu-settings-group auto-add-pages">
  911. <legend class="menu-settings-group-name howto"><?php _e( 'Auto add pages' ); ?></legend>
  912. <div class="menu-settings-input checkbox-input">
  913. <input type="checkbox"<?php checked( $auto_add ); ?> name="auto-add-pages" id="auto-add-pages" value="1" /> <label for="auto-add-pages"><?php printf( __( 'Automatically add new top-level pages to this menu' ), esc_url( admin_url( 'edit.php?post_type=page' ) ) ); ?></label>
  914. </div>
  915. </fieldset>
  916. <?php if ( current_theme_supports( 'menus' ) ) : ?>
  917. <fieldset class="menu-settings-group menu-theme-locations">
  918. <legend class="menu-settings-group-name howto"><?php _e( 'Display location' ); ?></legend>
  919. <?php
  920. foreach ( $locations as $location => $description ) :
  921. $checked = false;
  922. if ( isset( $menu_locations[ $location ] )
  923. && 0 !== $nav_menu_selected_id
  924. && $menu_locations[ $location ] === $nav_menu_selected_id
  925. ) {
  926. $checked = true;
  927. }
  928. ?>
  929. <div class="menu-settings-input checkbox-input">
  930. <input type="checkbox"<?php checked( $checked ); ?> name="menu-locations[<?php echo esc_attr( $location ); ?>]" id="locations-<?php echo esc_attr( $location ); ?>" value="<?php echo esc_attr( $nav_menu_selected_id ); ?>" />
  931. <label for="locations-<?php echo esc_attr( $location ); ?>"><?php echo $description; ?></label>
  932. <?php if ( ! empty( $menu_locations[ $location ] ) && $menu_locations[ $location ] !== $nav_menu_selected_id ) : ?>
  933. <span class="theme-location-set">
  934. <?php
  935. printf(
  936. /* translators: %s: Menu name. */
  937. _x( '(Currently set to: %s)', 'menu location' ),
  938. wp_get_nav_menu_object( $menu_locations[ $location ] )->name
  939. );
  940. ?>
  941. </span>
  942. <?php endif; ?>
  943. </div>
  944. <?php endforeach; ?>
  945. </fieldset>
  946. <?php endif; ?>
  947. </div>
  948. </div><!-- /#post-body-content -->
  949. </div><!-- /#post-body -->
  950. <div id="nav-menu-footer">
  951. <div class="major-publishing-actions wp-clearfix">
  952. <?php if ( $menu_count > 0 ) : ?>
  953. <?php if ( $add_new_screen ) : ?>
  954. <span class="cancel-action">
  955. <a class="submitcancel cancellation menu-cancel" href="<?php echo esc_url( admin_url( 'nav-menus.php' ) ); ?>"><?php _e( 'Cancel' ); ?></a>
  956. </span><!-- END .cancel-action -->
  957. <?php else : ?>
  958. <span class="delete-action">
  959. <a class="submitdelete deletion menu-delete" href="
  960. <?php
  961. echo esc_url(
  962. wp_nonce_url(
  963. add_query_arg(
  964. array(
  965. 'action' => 'delete',
  966. 'menu' => $nav_menu_selected_id,
  967. ),
  968. admin_url( 'nav-menus.php' )
  969. ),
  970. 'delete-nav_menu-' . $nav_menu_selected_id
  971. )
  972. );
  973. ?>
  974. "><?php _e( 'Delete Menu' ); ?></a>
  975. </span><!-- END .delete-action -->
  976. <?php endif; ?>
  977. <?php endif; ?>
  978. <div class="publishing-action">
  979. <?php submit_button( empty( $nav_menu_selected_id ) ? __( 'Create Menu' ) : __( 'Save Menu' ), 'primary large menu-save', 'save_menu', false, array( 'id' => 'save_menu_footer' ) ); ?>
  980. </div><!-- END .publishing-action -->
  981. </div><!-- END .major-publishing-actions -->
  982. </div><!-- /#nav-menu-footer -->
  983. </div><!-- /.menu-edit -->
  984. </form><!-- /#update-nav-menu -->
  985. </div><!-- /#menu-management -->
  986. </div><!-- /#menu-management-liquid -->
  987. </div><!-- /#nav-menus-frame -->
  988. <?php endif; ?>
  989. </div><!-- /.wrap-->
  990. <?php require_once ABSPATH . 'wp-admin/admin-footer.php'; ?>