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

+ 10 - 0
capacitor.config.ts

@@ -0,0 +1,10 @@
1
+import { CapacitorConfig } from '@capacitor/cli';
2
+
3
+const config: CapacitorConfig = {
4
+  appId: 'io.ionic.starter',
5
+  appName: 'Tune2Win',
6
+  webDir: 'www',
7
+  bundledWebRuntime: false
8
+};
9
+
10
+export default config;

+ 37 - 0
e2e/protractor.conf.js

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

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

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

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

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

+ 12 - 0
e2e/tsconfig.json

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

+ 7 - 0
ionic.config.json

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

+ 9 - 0
ios/.gitignore

@@ -0,0 +1,9 @@
1
+App/build
2
+App/Pods
3
+App/Podfile.lock
4
+App/App/public
5
+DerivedData
6
+xcuserdata
7
+
8
+# Cordova plugins for Capacitor
9
+capacitor-cordova-ios-plugins

+ 416 - 0
ios/App/App.xcodeproj/project.pbxproj

@@ -0,0 +1,416 @@
1
+// !$*UTF8*$!
2
+{
3
+	archiveVersion = 1;
4
+	classes = {
5
+	};
6
+	objectVersion = 48;
7
+	objects = {
8
+
9
+/* Begin PBXBuildFile section */
10
+		1EDEF1D527C21110006F183C /* HealthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1EDEF1D427C21110006F183C /* HealthKit.framework */; };
11
+		2FAD9763203C412B000D30F8 /* config.xml in Resources */ = {isa = PBXBuildFile; fileRef = 2FAD9762203C412B000D30F8 /* config.xml */; };
12
+		50379B232058CBB4000EE86E /* capacitor.config.json in Resources */ = {isa = PBXBuildFile; fileRef = 50379B222058CBB4000EE86E /* capacitor.config.json */; };
13
+		504EC3081FED79650016851F /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 504EC3071FED79650016851F /* AppDelegate.swift */; };
14
+		504EC30D1FED79650016851F /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30B1FED79650016851F /* Main.storyboard */; };
15
+		504EC30F1FED79650016851F /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 504EC30E1FED79650016851F /* Assets.xcassets */; };
16
+		504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC3101FED79650016851F /* LaunchScreen.storyboard */; };
17
+		50B271D11FEDC1A000F3C39B /* public in Resources */ = {isa = PBXBuildFile; fileRef = 50B271D01FEDC1A000F3C39B /* public */; };
18
+		A084ECDBA7D38E1E42DFC39D /* Pods_App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */; };
19
+/* End PBXBuildFile section */
20
+
21
+/* Begin PBXFileReference section */
22
+		1EDEF1D327C21110006F183C /* App.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = App.entitlements; sourceTree = "<group>"; };
23
+		1EDEF1D427C21110006F183C /* HealthKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = HealthKit.framework; path = System/Library/Frameworks/HealthKit.framework; sourceTree = SDKROOT; };
24
+		2FAD9762203C412B000D30F8 /* config.xml */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = config.xml; sourceTree = "<group>"; };
25
+		50379B222058CBB4000EE86E /* capacitor.config.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = capacitor.config.json; sourceTree = "<group>"; };
26
+		504EC3041FED79650016851F /* App.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = App.app; sourceTree = BUILT_PRODUCTS_DIR; };
27
+		504EC3071FED79650016851F /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
28
+		504EC30C1FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
29
+		504EC30E1FED79650016851F /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
30
+		504EC3111FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
31
+		504EC3131FED79650016851F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
32
+		50B271D01FEDC1A000F3C39B /* public */ = {isa = PBXFileReference; lastKnownFileType = folder; path = public; sourceTree = "<group>"; };
33
+		AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_App.framework; sourceTree = BUILT_PRODUCTS_DIR; };
34
+		AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.release.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.release.xcconfig"; sourceTree = "<group>"; };
35
+		FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.debug.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.debug.xcconfig"; sourceTree = "<group>"; };
36
+/* End PBXFileReference section */
37
+
38
+/* Begin PBXFrameworksBuildPhase section */
39
+		504EC3011FED79650016851F /* Frameworks */ = {
40
+			isa = PBXFrameworksBuildPhase;
41
+			buildActionMask = 2147483647;
42
+			files = (
43
+				A084ECDBA7D38E1E42DFC39D /* Pods_App.framework in Frameworks */,
44
+				1EDEF1D527C21110006F183C /* HealthKit.framework in Frameworks */,
45
+			);
46
+			runOnlyForDeploymentPostprocessing = 0;
47
+		};
48
+/* End PBXFrameworksBuildPhase section */
49
+
50
+/* Begin PBXGroup section */
51
+		27E2DDA53C4D2A4D1A88CE4A /* Frameworks */ = {
52
+			isa = PBXGroup;
53
+			children = (
54
+				1EDEF1D427C21110006F183C /* HealthKit.framework */,
55
+				AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */,
56
+			);
57
+			name = Frameworks;
58
+			sourceTree = "<group>";
59
+		};
60
+		504EC2FB1FED79650016851F = {
61
+			isa = PBXGroup;
62
+			children = (
63
+				504EC3061FED79650016851F /* App */,
64
+				504EC3051FED79650016851F /* Products */,
65
+				7F8756D8B27F46E3366F6CEA /* Pods */,
66
+				27E2DDA53C4D2A4D1A88CE4A /* Frameworks */,
67
+			);
68
+			sourceTree = "<group>";
69
+		};
70
+		504EC3051FED79650016851F /* Products */ = {
71
+			isa = PBXGroup;
72
+			children = (
73
+				504EC3041FED79650016851F /* App.app */,
74
+			);
75
+			name = Products;
76
+			sourceTree = "<group>";
77
+		};
78
+		504EC3061FED79650016851F /* App */ = {
79
+			isa = PBXGroup;
80
+			children = (
81
+				1EDEF1D327C21110006F183C /* App.entitlements */,
82
+				50379B222058CBB4000EE86E /* capacitor.config.json */,
83
+				504EC3071FED79650016851F /* AppDelegate.swift */,
84
+				504EC30B1FED79650016851F /* Main.storyboard */,
85
+				504EC30E1FED79650016851F /* Assets.xcassets */,
86
+				504EC3101FED79650016851F /* LaunchScreen.storyboard */,
87
+				504EC3131FED79650016851F /* Info.plist */,
88
+				2FAD9762203C412B000D30F8 /* config.xml */,
89
+				50B271D01FEDC1A000F3C39B /* public */,
90
+			);
91
+			path = App;
92
+			sourceTree = "<group>";
93
+		};
94
+		7F8756D8B27F46E3366F6CEA /* Pods */ = {
95
+			isa = PBXGroup;
96
+			children = (
97
+				FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */,
98
+				AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */,
99
+			);
100
+			name = Pods;
101
+			sourceTree = "<group>";
102
+		};
103
+/* End PBXGroup section */
104
+
105
+/* Begin PBXNativeTarget section */
106
+		504EC3031FED79650016851F /* App */ = {
107
+			isa = PBXNativeTarget;
108
+			buildConfigurationList = 504EC3161FED79650016851F /* Build configuration list for PBXNativeTarget "App" */;
109
+			buildPhases = (
110
+				6634F4EFEBD30273BCE97C65 /* [CP] Check Pods Manifest.lock */,
111
+				504EC3001FED79650016851F /* Sources */,
112
+				504EC3011FED79650016851F /* Frameworks */,
113
+				504EC3021FED79650016851F /* Resources */,
114
+				9592DBEFFC6D2A0C8D5DEB22 /* [CP] Embed Pods Frameworks */,
115
+			);
116
+			buildRules = (
117
+			);
118
+			dependencies = (
119
+			);
120
+			name = App;
121
+			productName = App;
122
+			productReference = 504EC3041FED79650016851F /* App.app */;
123
+			productType = "com.apple.product-type.application";
124
+		};
125
+/* End PBXNativeTarget section */
126
+
127
+/* Begin PBXProject section */
128
+		504EC2FC1FED79650016851F /* Project object */ = {
129
+			isa = PBXProject;
130
+			attributes = {
131
+				LastSwiftUpdateCheck = 0920;
132
+				LastUpgradeCheck = 0920;
133
+				TargetAttributes = {
134
+					504EC3031FED79650016851F = {
135
+						CreatedOnToolsVersion = 9.2;
136
+						LastSwiftMigration = 1100;
137
+						ProvisioningStyle = Automatic;
138
+					};
139
+				};
140
+			};
141
+			buildConfigurationList = 504EC2FF1FED79650016851F /* Build configuration list for PBXProject "App" */;
142
+			compatibilityVersion = "Xcode 8.0";
143
+			developmentRegion = en;
144
+			hasScannedForEncodings = 0;
145
+			knownRegions = (
146
+				en,
147
+				Base,
148
+			);
149
+			mainGroup = 504EC2FB1FED79650016851F;
150
+			productRefGroup = 504EC3051FED79650016851F /* Products */;
151
+			projectDirPath = "";
152
+			projectRoot = "";
153
+			targets = (
154
+				504EC3031FED79650016851F /* App */,
155
+			);
156
+		};
157
+/* End PBXProject section */
158
+
159
+/* Begin PBXResourcesBuildPhase section */
160
+		504EC3021FED79650016851F /* Resources */ = {
161
+			isa = PBXResourcesBuildPhase;
162
+			buildActionMask = 2147483647;
163
+			files = (
164
+				504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */,
165
+				50B271D11FEDC1A000F3C39B /* public in Resources */,
166
+				504EC30F1FED79650016851F /* Assets.xcassets in Resources */,
167
+				50379B232058CBB4000EE86E /* capacitor.config.json in Resources */,
168
+				504EC30D1FED79650016851F /* Main.storyboard in Resources */,
169
+				2FAD9763203C412B000D30F8 /* config.xml in Resources */,
170
+			);
171
+			runOnlyForDeploymentPostprocessing = 0;
172
+		};
173
+/* End PBXResourcesBuildPhase section */
174
+
175
+/* Begin PBXShellScriptBuildPhase section */
176
+		6634F4EFEBD30273BCE97C65 /* [CP] Check Pods Manifest.lock */ = {
177
+			isa = PBXShellScriptBuildPhase;
178
+			buildActionMask = 2147483647;
179
+			files = (
180
+			);
181
+			inputPaths = (
182
+				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
183
+				"${PODS_ROOT}/Manifest.lock",
184
+			);
185
+			name = "[CP] Check Pods Manifest.lock";
186
+			outputPaths = (
187
+				"$(DERIVED_FILE_DIR)/Pods-App-checkManifestLockResult.txt",
188
+			);
189
+			runOnlyForDeploymentPostprocessing = 0;
190
+			shellPath = /bin/sh;
191
+			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
192
+			showEnvVarsInLog = 0;
193
+		};
194
+		9592DBEFFC6D2A0C8D5DEB22 /* [CP] Embed Pods Frameworks */ = {
195
+			isa = PBXShellScriptBuildPhase;
196
+			buildActionMask = 2147483647;
197
+			files = (
198
+			);
199
+			inputPaths = (
200
+			);
201
+			name = "[CP] Embed Pods Frameworks";
202
+			outputPaths = (
203
+			);
204
+			runOnlyForDeploymentPostprocessing = 0;
205
+			shellPath = /bin/sh;
206
+			shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-App/Pods-App-frameworks.sh\"\n";
207
+			showEnvVarsInLog = 0;
208
+		};
209
+/* End PBXShellScriptBuildPhase section */
210
+
211
+/* Begin PBXSourcesBuildPhase section */
212
+		504EC3001FED79650016851F /* Sources */ = {
213
+			isa = PBXSourcesBuildPhase;
214
+			buildActionMask = 2147483647;
215
+			files = (
216
+				504EC3081FED79650016851F /* AppDelegate.swift in Sources */,
217
+			);
218
+			runOnlyForDeploymentPostprocessing = 0;
219
+		};
220
+/* End PBXSourcesBuildPhase section */
221
+
222
+/* Begin PBXVariantGroup section */
223
+		504EC30B1FED79650016851F /* Main.storyboard */ = {
224
+			isa = PBXVariantGroup;
225
+			children = (
226
+				504EC30C1FED79650016851F /* Base */,
227
+			);
228
+			name = Main.storyboard;
229
+			sourceTree = "<group>";
230
+		};
231
+		504EC3101FED79650016851F /* LaunchScreen.storyboard */ = {
232
+			isa = PBXVariantGroup;
233
+			children = (
234
+				504EC3111FED79650016851F /* Base */,
235
+			);
236
+			name = LaunchScreen.storyboard;
237
+			sourceTree = "<group>";
238
+		};
239
+/* End PBXVariantGroup section */
240
+
241
+/* Begin XCBuildConfiguration section */
242
+		504EC3141FED79650016851F /* Debug */ = {
243
+			isa = XCBuildConfiguration;
244
+			buildSettings = {
245
+				ALWAYS_SEARCH_USER_PATHS = NO;
246
+				CLANG_ANALYZER_NONNULL = YES;
247
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
248
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
249
+				CLANG_CXX_LIBRARY = "libc++";
250
+				CLANG_ENABLE_MODULES = YES;
251
+				CLANG_ENABLE_OBJC_ARC = YES;
252
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
253
+				CLANG_WARN_BOOL_CONVERSION = YES;
254
+				CLANG_WARN_COMMA = YES;
255
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
256
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
257
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
258
+				CLANG_WARN_EMPTY_BODY = YES;
259
+				CLANG_WARN_ENUM_CONVERSION = YES;
260
+				CLANG_WARN_INFINITE_RECURSION = YES;
261
+				CLANG_WARN_INT_CONVERSION = YES;
262
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
263
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
264
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
265
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
266
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
267
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
268
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
269
+				CLANG_WARN_UNREACHABLE_CODE = YES;
270
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
271
+				CODE_SIGN_IDENTITY = "iPhone Developer";
272
+				COPY_PHASE_STRIP = NO;
273
+				DEBUG_INFORMATION_FORMAT = dwarf;
274
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
275
+				ENABLE_TESTABILITY = YES;
276
+				GCC_C_LANGUAGE_STANDARD = gnu11;
277
+				GCC_DYNAMIC_NO_PIC = NO;
278
+				GCC_NO_COMMON_BLOCKS = YES;
279
+				GCC_OPTIMIZATION_LEVEL = 0;
280
+				GCC_PREPROCESSOR_DEFINITIONS = (
281
+					"DEBUG=1",
282
+					"$(inherited)",
283
+				);
284
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
285
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
286
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
287
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
288
+				GCC_WARN_UNUSED_FUNCTION = YES;
289
+				GCC_WARN_UNUSED_VARIABLE = YES;
290
+				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
291
+				MTL_ENABLE_DEBUG_INFO = YES;
292
+				ONLY_ACTIVE_ARCH = YES;
293
+				SDKROOT = iphoneos;
294
+				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
295
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
296
+			};
297
+			name = Debug;
298
+		};
299
+		504EC3151FED79650016851F /* Release */ = {
300
+			isa = XCBuildConfiguration;
301
+			buildSettings = {
302
+				ALWAYS_SEARCH_USER_PATHS = NO;
303
+				CLANG_ANALYZER_NONNULL = YES;
304
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
305
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
306
+				CLANG_CXX_LIBRARY = "libc++";
307
+				CLANG_ENABLE_MODULES = YES;
308
+				CLANG_ENABLE_OBJC_ARC = YES;
309
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
310
+				CLANG_WARN_BOOL_CONVERSION = YES;
311
+				CLANG_WARN_COMMA = YES;
312
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
313
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
314
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
315
+				CLANG_WARN_EMPTY_BODY = YES;
316
+				CLANG_WARN_ENUM_CONVERSION = YES;
317
+				CLANG_WARN_INFINITE_RECURSION = YES;
318
+				CLANG_WARN_INT_CONVERSION = YES;
319
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
320
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
321
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
322
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
323
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
324
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
325
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
326
+				CLANG_WARN_UNREACHABLE_CODE = YES;
327
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
328
+				CODE_SIGN_IDENTITY = "iPhone Developer";
329
+				COPY_PHASE_STRIP = NO;
330
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
331
+				ENABLE_NS_ASSERTIONS = NO;
332
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
333
+				GCC_C_LANGUAGE_STANDARD = gnu11;
334
+				GCC_NO_COMMON_BLOCKS = YES;
335
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
336
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
337
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
338
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
339
+				GCC_WARN_UNUSED_FUNCTION = YES;
340
+				GCC_WARN_UNUSED_VARIABLE = YES;
341
+				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
342
+				MTL_ENABLE_DEBUG_INFO = NO;
343
+				SDKROOT = iphoneos;
344
+				SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
345
+				VALIDATE_PRODUCT = YES;
346
+			};
347
+			name = Release;
348
+		};
349
+		504EC3171FED79650016851F /* Debug */ = {
350
+			isa = XCBuildConfiguration;
351
+			baseConfigurationReference = FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */;
352
+			buildSettings = {
353
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
354
+				CODE_SIGN_ENTITLEMENTS = App/App.entitlements;
355
+				CODE_SIGN_IDENTITY = "Apple Development";
356
+				CODE_SIGN_STYLE = Automatic;
357
+				DEVELOPMENT_TEAM = 5QTJEGL2H2;
358
+				INFOPLIST_FILE = App/Info.plist;
359
+				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
360
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
361
+				OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
362
+				PRODUCT_BUNDLE_IDENTIFIER = net.simplico.tune2win;
363
+				PRODUCT_NAME = "$(TARGET_NAME)";
364
+				PROVISIONING_PROFILE_SPECIFIER = "";
365
+				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
366
+				SWIFT_VERSION = 5.0;
367
+				TARGETED_DEVICE_FAMILY = "1,2";
368
+			};
369
+			name = Debug;
370
+		};
371
+		504EC3181FED79650016851F /* Release */ = {
372
+			isa = XCBuildConfiguration;
373
+			baseConfigurationReference = AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */;
374
+			buildSettings = {
375
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
376
+				CODE_SIGN_ENTITLEMENTS = App/App.entitlements;
377
+				CODE_SIGN_IDENTITY = "Apple Development";
378
+				CODE_SIGN_STYLE = Automatic;
379
+				DEVELOPMENT_TEAM = 5QTJEGL2H2;
380
+				INFOPLIST_FILE = App/Info.plist;
381
+				IPHONEOS_DEPLOYMENT_TARGET = 12.0;
382
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
383
+				PRODUCT_BUNDLE_IDENTIFIER = net.simplico.tune2win;
384
+				PRODUCT_NAME = "$(TARGET_NAME)";
385
+				PROVISIONING_PROFILE_SPECIFIER = "";
386
+				SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
387
+				SWIFT_VERSION = 5.0;
388
+				TARGETED_DEVICE_FAMILY = "1,2";
389
+			};
390
+			name = Release;
391
+		};
392
+/* End XCBuildConfiguration section */
393
+
394
+/* Begin XCConfigurationList section */
395
+		504EC2FF1FED79650016851F /* Build configuration list for PBXProject "App" */ = {
396
+			isa = XCConfigurationList;
397
+			buildConfigurations = (
398
+				504EC3141FED79650016851F /* Debug */,
399
+				504EC3151FED79650016851F /* Release */,
400
+			);
401
+			defaultConfigurationIsVisible = 0;
402
+			defaultConfigurationName = Release;
403
+		};
404
+		504EC3161FED79650016851F /* Build configuration list for PBXNativeTarget "App" */ = {
405
+			isa = XCConfigurationList;
406
+			buildConfigurations = (
407
+				504EC3171FED79650016851F /* Debug */,
408
+				504EC3181FED79650016851F /* Release */,
409
+			);
410
+			defaultConfigurationIsVisible = 0;
411
+			defaultConfigurationName = Release;
412
+		};
413
+/* End XCConfigurationList section */
414
+	};
415
+	rootObject = 504EC2FC1FED79650016851F /* Project object */;
416
+}

+ 7 - 0
ios/App/App.xcodeproj/project.xcworkspace/contents.xcworkspacedata

@@ -0,0 +1,7 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<Workspace
3
+   version = "1.0">
4
+   <FileRef
5
+      location = "self:App.xcodeproj">
6
+   </FileRef>
7
+</Workspace>

+ 10 - 0
ios/App/App.xcworkspace/contents.xcworkspacedata

@@ -0,0 +1,10 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<Workspace
3
+   version = "1.0">
4
+   <FileRef
5
+      location = "group:App.xcodeproj">
6
+   </FileRef>
7
+   <FileRef
8
+      location = "group:Pods/Pods.xcodeproj">
9
+   </FileRef>
10
+</Workspace>

+ 8 - 0
ios/App/App.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist

@@ -0,0 +1,8 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+<plist version="1.0">
4
+<dict>
5
+	<key>IDEDidComputeMac32BitWarning</key>
6
+	<true/>
7
+</dict>
8
+</plist>

+ 10 - 0
ios/App/App/App.entitlements

@@ -0,0 +1,10 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+<plist version="1.0">
4
+<dict>
5
+	<key>com.apple.developer.healthkit</key>
6
+	<true/>
7
+	<key>com.apple.developer.healthkit.access</key>
8
+	<array/>
9
+</dict>
10
+</plist>

+ 60 - 0
ios/App/App/AppDelegate.swift

@@ -0,0 +1,60 @@
1
+import UIKit
2
+import Capacitor
3
+
4
+@UIApplicationMain
5
+class AppDelegate: UIResponder, UIApplicationDelegate {
6
+
7
+    var window: UIWindow?
8
+
9
+    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
10
+        // Override point for customization after application launch.
11
+        return true
12
+    }
13
+
14
+    func applicationWillResignActive(_ application: UIApplication) {
15
+        // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
16
+        // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
17
+    }
18
+
19
+    func applicationDidEnterBackground(_ application: UIApplication) {
20
+        // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
21
+        // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
22
+    }
23
+
24
+    func applicationWillEnterForeground(_ application: UIApplication) {
25
+        // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
26
+    }
27
+
28
+    func applicationDidBecomeActive(_ application: UIApplication) {
29
+        // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
30
+    }
31
+
32
+    func applicationWillTerminate(_ application: UIApplication) {
33
+        // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
34
+    }
35
+
36
+    func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey: Any] = [:]) -> Bool {
37
+        // Called when the app was launched with a url. Feel free to add additional processing here,
38
+        // but if you want the App API to support tracking app url opens, make sure to keep this call
39
+        return ApplicationDelegateProxy.shared.application(app, open: url, options: options)
40
+    }
41
+
42
+    func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
43
+        // Called when the app was launched with an activity, including Universal Links.
44
+        // Feel free to add additional processing here, but if you want the App API to support
45
+        // tracking app url opens, make sure to keep this call
46
+        return ApplicationDelegateProxy.shared.application(application, continue: userActivity, restorationHandler: restorationHandler)
47
+    }
48
+
49
+    override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
50
+        super.touchesBegan(touches, with: event)
51
+
52
+        let statusBarRect = UIApplication.shared.statusBarFrame
53
+        guard let touchPoint = event?.allTouches?.first?.location(in: self.window) else { return }
54
+
55
+        if statusBarRect.contains(touchPoint) {
56
+            NotificationCenter.default.post(name: .capacitorStatusBarTapped, object: nil)
57
+        }
58
+    }
59
+
60
+}

BIN
ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@1x.png


BIN
ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x-1.png


BIN
ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@2x.png


BIN
ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-20x20@3x.png


BIN
ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@1x.png


BIN
ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x-1.png


BIN
ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@2x.png


BIN
ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-29x29@3x.png


BIN
ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@1x.png


BIN
ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x-1.png


BIN
ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@2x.png


BIN
ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-40x40@3x.png


BIN
ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-512@2x.png


BIN
ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@2x.png


BIN
ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-60x60@3x.png


BIN
ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@1x.png


BIN
ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-76x76@2x.png


BIN
ios/App/App/Assets.xcassets/AppIcon.appiconset/AppIcon-83.5x83.5@2x.png


+ 116 - 0
ios/App/App/Assets.xcassets/AppIcon.appiconset/Contents.json

@@ -0,0 +1,116 @@
1
+{
2
+  "images" : [
3
+    {
4
+      "size" : "20x20",
5
+      "idiom" : "iphone",
6
+      "filename" : "AppIcon-20x20@2x.png",
7
+      "scale" : "2x"
8
+    },
9
+    {
10
+      "size" : "20x20",
11
+      "idiom" : "iphone",
12
+      "filename" : "AppIcon-20x20@3x.png",
13
+      "scale" : "3x"
14
+    },
15
+    {
16
+      "size" : "29x29",
17
+      "idiom" : "iphone",
18
+      "filename" : "AppIcon-29x29@2x-1.png",
19
+      "scale" : "2x"
20
+    },
21
+    {
22
+      "size" : "29x29",
23
+      "idiom" : "iphone",
24
+      "filename" : "AppIcon-29x29@3x.png",
25
+      "scale" : "3x"
26
+    },
27
+    {
28
+      "size" : "40x40",
29
+      "idiom" : "iphone",
30
+      "filename" : "AppIcon-40x40@2x.png",
31
+      "scale" : "2x"
32
+    },
33
+    {
34
+      "size" : "40x40",
35
+      "idiom" : "iphone",
36
+      "filename" : "AppIcon-40x40@3x.png",
37
+      "scale" : "3x"
38
+    },
39
+    {
40
+      "size" : "60x60",
41
+      "idiom" : "iphone",
42
+      "filename" : "AppIcon-60x60@2x.png",
43
+      "scale" : "2x"
44
+    },
45
+    {
46
+      "size" : "60x60",
47
+      "idiom" : "iphone",
48
+      "filename" : "AppIcon-60x60@3x.png",
49
+      "scale" : "3x"
50
+    },
51
+    {
52
+      "size" : "20x20",
53
+      "idiom" : "ipad",
54
+      "filename" : "AppIcon-20x20@1x.png",
55
+      "scale" : "1x"
56
+    },
57
+    {
58
+      "size" : "20x20",
59
+      "idiom" : "ipad",
60
+      "filename" : "AppIcon-20x20@2x-1.png",
61
+      "scale" : "2x"
62
+    },
63
+    {
64
+      "size" : "29x29",
65
+      "idiom" : "ipad",
66
+      "filename" : "AppIcon-29x29@1x.png",
67
+      "scale" : "1x"
68
+    },
69
+    {
70
+      "size" : "29x29",
71
+      "idiom" : "ipad",
72
+      "filename" : "AppIcon-29x29@2x.png",
73
+      "scale" : "2x"
74
+    },
75
+    {
76
+      "size" : "40x40",
77
+      "idiom" : "ipad",
78
+      "filename" : "AppIcon-40x40@1x.png",
79
+      "scale" : "1x"
80
+    },
81
+    {
82
+      "size" : "40x40",
83
+      "idiom" : "ipad",
84
+      "filename" : "AppIcon-40x40@2x-1.png",
85
+      "scale" : "2x"
86
+    },
87
+    {
88
+      "size" : "76x76",
89
+      "idiom" : "ipad",
90
+      "filename" : "AppIcon-76x76@1x.png",
91
+      "scale" : "1x"
92
+    },
93
+    {
94
+      "size" : "76x76",
95
+      "idiom" : "ipad",
96
+      "filename" : "AppIcon-76x76@2x.png",
97
+      "scale" : "2x"
98
+    },
99
+    {
100
+      "size" : "83.5x83.5",
101
+      "idiom" : "ipad",
102
+      "filename" : "AppIcon-83.5x83.5@2x.png",
103
+      "scale" : "2x"
104
+    },
105
+    {
106
+      "size" : "1024x1024",
107
+      "idiom" : "ios-marketing",
108
+      "filename" : "AppIcon-512@2x.png",
109
+      "scale" : "1x"
110
+    }
111
+  ],
112
+  "info" : {
113
+    "version" : 1,
114
+    "author" : "xcode"
115
+  }
116
+}

+ 6 - 0
ios/App/App/Assets.xcassets/Contents.json

@@ -0,0 +1,6 @@
1
+{
2
+  "info" : {
3
+    "version" : 1,
4
+    "author" : "xcode"
5
+  }
6
+}

+ 23 - 0
ios/App/App/Assets.xcassets/Splash.imageset/Contents.json

@@ -0,0 +1,23 @@
1
+{
2
+  "images" : [
3
+    {
4
+      "idiom" : "universal",
5
+      "filename" : "splash-2732x2732-2.png",
6
+      "scale" : "1x"
7
+    },
8
+    {
9
+      "idiom" : "universal",
10
+      "filename" : "splash-2732x2732-1.png",
11
+      "scale" : "2x"
12
+    },
13
+    {
14
+      "idiom" : "universal",
15
+      "filename" : "splash-2732x2732.png",
16
+      "scale" : "3x"
17
+    }
18
+  ],
19
+  "info" : {
20
+    "version" : 1,
21
+    "author" : "xcode"
22
+  }
23
+}

BIN
ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png


BIN
ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png


BIN
ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png


+ 32 - 0
ios/App/App/Base.lproj/LaunchScreen.storyboard

@@ -0,0 +1,32 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17132" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
3
+    <device id="retina4_7" orientation="portrait" appearance="light"/>
4
+    <dependencies>
5
+        <deployment identifier="iOS"/>
6
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17105"/>
7
+        <capability name="System colors in document resources" minToolsVersion="11.0"/>
8
+        <capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
9
+    </dependencies>
10
+    <scenes>
11
+        <!--View Controller-->
12
+        <scene sceneID="EHf-IW-A2E">
13
+            <objects>
14
+                <viewController id="01J-lp-oVM" sceneMemberID="viewController">
15
+                    <imageView key="view" userInteractionEnabled="NO" contentMode="scaleAspectFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="Splash" id="snD-IY-ifK">
16
+                        <rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
17
+                        <autoresizingMask key="autoresizingMask"/>
18
+                        <color key="backgroundColor" systemColor="systemBackgroundColor"/>
19
+                    </imageView>
20
+                </viewController>
21
+                <placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
22
+            </objects>
23
+            <point key="canvasLocation" x="53" y="375"/>
24
+        </scene>
25
+    </scenes>
26
+    <resources>
27
+        <image name="Splash" width="1366" height="1366"/>
28
+        <systemColor name="systemBackgroundColor">
29
+            <color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
30
+        </systemColor>
31
+    </resources>
32
+</document>

+ 19 - 0
ios/App/App/Base.lproj/Main.storyboard

@@ -0,0 +1,19 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="14111" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
3
+    <device id="retina4_7" orientation="portrait">
4
+        <adaptation id="fullscreen"/>
5
+    </device>
6
+    <dependencies>
7
+        <deployment identifier="iOS"/>
8
+        <plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14088"/>
9
+    </dependencies>
10
+    <scenes>
11
+        <!--Bridge View Controller-->
12
+        <scene sceneID="tne-QT-ifu">
13
+            <objects>
14
+                <viewController id="BYZ-38-t0r" customClass="CAPBridgeViewController" customModule="Capacitor" sceneMemberID="viewController"/>
15
+                <placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
16
+            </objects>
17
+        </scene>
18
+    </scenes>
19
+</document>

+ 64 - 0
ios/App/App/Info.plist

@@ -0,0 +1,64 @@
1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+<plist version="1.0">
4
+<dict>
5
+	<key>CFBundleDevelopmentRegion</key>
6
+	<string>en</string>
7
+	<key>CFBundleDisplayName</key>
8
+	<string>Tune2Win</string>
9
+	<key>CFBundleExecutable</key>
10
+	<string>$(EXECUTABLE_NAME)</string>
11
+	<key>CFBundleIdentifier</key>
12
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
13
+	<key>CFBundleInfoDictionaryVersion</key>
14
+	<string>6.0</string>
15
+	<key>CFBundleName</key>
16
+	<string>$(PRODUCT_NAME)</string>
17
+	<key>CFBundlePackageType</key>
18
+	<string>APPL</string>
19
+	<key>CFBundleShortVersionString</key>
20
+	<string>1.0</string>
21
+	<key>CFBundleVersion</key>
22
+	<string>1</string>
23
+	<key>LSRequiresIPhoneOS</key>
24
+	<true/>
25
+	<key>NSAppTransportSecurity</key>
26
+	<dict>
27
+		<key>NSAllowsArbitraryLoads</key>
28
+		<true/>
29
+	</dict>
30
+    <key>NSContactsUsageDescription</key>
31
+    <string>Get Contact for your trainings</string>
32
+    <key>NSCalendarsUsageDescription</key>
33
+    <string>Access Calendar for trainings</string>
34
+	<key>NSHealthClinicalHealthRecordsShareUsageDescription</key>
35
+	<string>Get data for improve athlete</string>
36
+	<key>NSHealthShareUsageDescription</key>
37
+	<string>Get data for improve athlete</string>
38
+	<key>NSHealthUpdateUsageDescription</key>
39
+	<string>Get data for improve athlete</string>
40
+	<key>UILaunchStoryboardName</key>
41
+	<string>LaunchScreen</string>
42
+	<key>UIMainStoryboardFile</key>
43
+	<string>Main</string>
44
+	<key>UIRequiredDeviceCapabilities</key>
45
+	<array>
46
+		<string>armv7</string>
47
+	</array>
48
+	<key>UISupportedInterfaceOrientations</key>
49
+	<array>
50
+		<string>UIInterfaceOrientationPortrait</string>
51
+		<string>UIInterfaceOrientationLandscapeLeft</string>
52
+		<string>UIInterfaceOrientationLandscapeRight</string>
53
+	</array>
54
+	<key>UISupportedInterfaceOrientations~ipad</key>
55
+	<array>
56
+		<string>UIInterfaceOrientationPortrait</string>
57
+		<string>UIInterfaceOrientationPortraitUpsideDown</string>
58
+		<string>UIInterfaceOrientationLandscapeLeft</string>
59
+		<string>UIInterfaceOrientationLandscapeRight</string>
60
+	</array>
61
+	<key>UIViewControllerBasedStatusBarAppearance</key>
62
+	<true/>
63
+</dict>
64
+</plist>

+ 9 - 0
ios/App/App/capacitor.config.json

@@ -0,0 +1,9 @@
1
+{
2
+	"appId": "io.ionic.starter",
3
+	"appName": "Tune2Win",
4
+	"webDir": "www",
5
+	"bundledWebRuntime": false,
6
+	"server": {
7
+		"url": "http://192.168.1.38:8100"
8
+	}
9
+}

+ 14 - 0
ios/App/App/config.xml

@@ -0,0 +1,14 @@
1
+<?xml version='1.0' encoding='utf-8'?>
2
+<widget version="1.0.0" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0">
3
+  <access origin="*" />
4
+  
5
+  <feature name="Calendar">
6
+    <param name="ios-package" value="Calendar"/>
7
+  </feature>
8
+
9
+  <feature name="HealthKit">
10
+    <param name="ios-package" value="HealthKit"/>
11
+  </feature>
12
+
13
+  
14
+</widget>

+ 22 - 0
ios/App/Podfile

@@ -0,0 +1,22 @@
1
+platform :ios, '12.0'
2
+use_frameworks!
3
+
4
+# workaround to avoid Xcode caching of Pods that requires
5
+# Product -> Clean Build Folder after new Cordova plugins installed
6
+# Requires CocoaPods 1.6 or newer
7
+install! 'cocoapods', :disable_input_output_paths => true
8
+
9
+def capacitor_pods
10
+  pod 'Capacitor', :path => '../../node_modules/@capacitor/ios'
11
+  pod 'CapacitorCordova', :path => '../../node_modules/@capacitor/ios'
12
+  pod 'CapacitorApp', :path => '../../node_modules/@capacitor/app'
13
+  pod 'CapacitorHaptics', :path => '../../node_modules/@capacitor/haptics'
14
+  pod 'CapacitorKeyboard', :path => '../../node_modules/@capacitor/keyboard'
15
+  pod 'CapacitorStatusBar', :path => '../../node_modules/@capacitor/status-bar'
16
+  pod 'CordovaPlugins', :path => '../capacitor-cordova-ios-plugins'
17
+end
18
+
19
+target 'App' do
20
+  capacitor_pods
21
+  # Add your Pods here
22
+end

+ 44 - 0
karma.conf.js

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

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 39378 - 0
package-lock.json


+ 75 - 0
package.json

@@ -0,0 +1,75 @@
1
+{
2
+  "name": "Tune2Win",
3
+  "version": "0.0.1",
4
+  "author": "Ionic Framework",
5
+  "homepage": "https://ionicframework.com/",
6
+  "scripts": {
7
+    "ng": "ng",
8
+    "start": "ng serve",
9
+    "build": "ng build",
10
+    "test": "ng test",
11
+    "lint": "ng lint",
12
+    "e2e": "ng e2e"
13
+  },
14
+  "private": true,
15
+  "dependencies": {
16
+    "@angular/common": "~13.0.0",
17
+    "@angular/core": "~13.0.0",
18
+    "@angular/forms": "~13.0.0",
19
+    "@angular/platform-browser": "~13.0.0",
20
+    "@angular/platform-browser-dynamic": "~13.0.0",
21
+    "@angular/router": "~13.0.0",
22
+    "@awesome-cordova-plugins/calendar": "^5.39.1",
23
+    "@awesome-cordova-plugins/health": "^5.39.1",
24
+    "@capacitor/app": "1.1.0",
25
+    "@capacitor/core": "3.4.1",
26
+    "@capacitor/haptics": "1.1.4",
27
+    "@capacitor/ios": "3.4.1",
28
+    "@capacitor/keyboard": "1.2.2",
29
+    "@capacitor/status-bar": "1.0.8",
30
+    "@ionic/angular": "^6.0.0",
31
+    "@ionic/storage-angular": "^3.0.6",
32
+    "chart.js": "^3.7.1",
33
+    "cordova-plugin-calendar": "^5.1.6",
34
+    "cordova-plugin-health": "^2.0.3",
35
+    "date-fns": "^2.28.0",
36
+    "ng2-charts": "^3.0.0-rc.7",
37
+    "rxjs": "~6.6.0",
38
+    "tslib": "^2.2.0",
39
+    "zone.js": "~0.11.4"
40
+  },
41
+  "devDependencies": {
42
+    "@angular-devkit/build-angular": "~13.0.1",
43
+    "@angular-eslint/builder": "~13.0.1",
44
+    "@angular-eslint/eslint-plugin": "~13.0.1",
45
+    "@angular-eslint/eslint-plugin-template": "~13.0.1",
46
+    "@angular-eslint/template-parser": "~13.0.1",
47
+    "@angular/cli": "~13.0.1",
48
+    "@angular/compiler": "~13.0.0",
49
+    "@angular/compiler-cli": "~13.0.0",
50
+    "@angular/language-service": "~13.0.0",
51
+    "@capacitor/cli": "3.4.1",
52
+    "@ionic/angular-toolkit": "^5.0.0",
53
+    "@types/jasmine": "~3.6.0",
54
+    "@types/jasminewd2": "~2.0.3",
55
+    "@types/node": "^12.11.1",
56
+    "@typescript-eslint/eslint-plugin": "5.3.0",
57
+    "@typescript-eslint/parser": "5.3.0",
58
+    "eslint": "^7.6.0",
59
+    "eslint-plugin-import": "2.22.1",
60
+    "eslint-plugin-jsdoc": "30.7.6",
61
+    "eslint-plugin-prefer-arrow": "1.2.2",
62
+    "jasmine-core": "~3.8.0",
63
+    "jasmine-spec-reporter": "~5.0.0",
64
+    "karma": "~6.3.2",
65
+    "karma-chrome-launcher": "~3.1.0",
66
+    "karma-coverage": "~2.0.3",
67
+    "karma-coverage-istanbul-reporter": "~3.0.2",
68
+    "karma-jasmine": "~4.0.0",
69
+    "karma-jasmine-html-reporter": "^1.5.0",
70
+    "protractor": "~7.0.0",
71
+    "ts-node": "~8.3.0",
72
+    "typescript": "~4.4.4"
73
+  },
74
+  "description": "An Ionic project"
75
+}

BIN
resources/icon.png


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


BIN
resources/ios/icon/icon.png


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


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


+ 0 - 0
resources/splash.png


Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels

tum/whitesports - Gogs: Simplico Git Service

説明なし

easy-wp-smtp.php 25KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698
  1. <?php
  2. use PHPMailer\PHPMailer\PHPMailer;
  3. use PHPMailer\PHPMailer\Exception;
  4. /*
  5. Plugin Name: Easy WP SMTP
  6. Version: 1.4.7
  7. Plugin URI: https://wp-ecommerce.net/easy-wordpress-smtp-send-emails-from-your-wordpress-site-using-a-smtp-server-2197
  8. Author: wpecommerce, alexanderfoxc
  9. Author URI: https://wp-ecommerce.net/
  10. Description: Send email via SMTP from your WordPress Blog
  11. Text Domain: easy-wp-smtp
  12. Domain Path: /languages
  13. */
  14. //Prefix/Slug - swpsmtp
  15. class EasyWPSMTP {
  16. public $opts;
  17. public $plugin_file;
  18. protected static $instance = null;
  19. public static $reset_log_str = "Easy WP SMTP debug log file\r\n\r\n";
  20. public function __construct() {
  21. $this->opts = get_option( 'swpsmtp_options' );
  22. $this->opts = ! is_array( $this->opts ) ? array() : $this->opts;
  23. $this->plugin_file = __FILE__;
  24. require_once 'class-easywpsmtp-utils.php';
  25. add_action( 'plugins_loaded', array( $this, 'plugins_loaded_handler' ) );
  26. add_filter( 'wp_mail', array( $this, 'wp_mail' ), 2147483647 );
  27. add_action( 'phpmailer_init', array( $this, 'init_smtp' ), 999 );
  28. add_action( 'admin_init', array( $this, 'admin_init' ) );
  29. if ( ! empty( $this->opts['smtp_settings']['enable_debug'] ) ) {
  30. add_action( 'wp_mail_failed', array( $this, 'wp_mail_failed' ) );
  31. }
  32. if ( is_admin() && ! ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) {
  33. require_once 'class-easywpsmtp-admin.php';
  34. register_activation_hook( __FILE__, array( $this, 'activate' ) );
  35. register_deactivation_hook( __FILE__, array( $this, 'deactivate' ) );
  36. register_uninstall_hook( __FILE__, 'swpsmtp_uninstall' );
  37. add_filter( 'plugin_action_links', array( $this, 'plugin_action_links' ), 10, 2 );
  38. add_filter( 'plugin_row_meta', array( $this, 'register_plugin_links' ), 10, 2 );
  39. add_action( 'admin_notices', array( $this, 'admin_notices' ) );
  40. }
  41. }
  42. public static function get_instance() {
  43. if ( null === self::$instance ) {
  44. self::$instance = new self();
  45. }
  46. return self::$instance;
  47. }
  48. public function wp_mail( $args ) {
  49. $domain = $this->is_domain_blocked();
  50. if ( false !== $domain && ( isset( $this->opts['block_all_emails'] ) && 1 === $this->opts['block_all_emails'] ) ) {
  51. $this->log(
  52. "\r\n------------------------------------------------------------------------------------------------------\r\n" .
  53. 'Domain check failed: website domain (' . $domain . ") is not in allowed domains list.\r\n" .
  54. "Following email not sent (block all emails option is enabled):\r\n" .
  55. 'To: ' . $args['to'] . '; Subject: ' . $args['subject'] . "\r\n" .
  56. "------------------------------------------------------------------------------------------------------\r\n\r\n"
  57. );
  58. return $args;
  59. }
  60. if ( ! empty( $this->opts['smtp_settings']['enable_debug'] ) ) {
  61. $line = sprintf(
  62. 'Headers: %s, To: %s, Subject: %s',
  63. ! empty( $args['headers'] && is_array( $args['headers'] ) ) ? implode( ' | ', $args['headers'] ) : '',
  64. ! empty( $args['to'] ) ? $args['to'] : '',
  65. ! empty( $args['subject'] ) ? $args['subject'] : ''
  66. );
  67. $this->log( $line . "\r\n" );
  68. }
  69. return $args;
  70. }
  71. public function wp_mail_failed( $wp_error ) {
  72. if ( ! empty( $wp_error->errors ) && ! empty( $wp_error->errors['wp_mail_failed'] ) && is_array( $wp_error->errors['wp_mail_failed'] ) ) {
  73. $this->log( '*** ' . implode( ' | ', $wp_error->errors['wp_mail_failed'] ) . " ***\r\n" );
  74. }
  75. }
  76. public function init_smtp( &$phpmailer ) {
  77. //check if SMTP credentials have been configured.
  78. if ( ! $this->credentials_configured() ) {
  79. return;
  80. }
  81. //check if Domain Check enabled
  82. $domain = $this->is_domain_blocked();
  83. if ( false !== $domain ) {
  84. //domain check failed
  85. //let's check if we have block all emails option enabled
  86. if ( isset( $this->opts['block_all_emails'] ) && 1 === $this->opts['block_all_emails'] ) {
  87. // it's enabled. Let's use gag mailer class that would prevent emails from being sent out.
  88. require_once 'class-easywpsmtp-gag-mailer.php';
  89. $phpmailer = new EasyWPSMTP_Gag_Mailer();
  90. } else {
  91. // it's disabled. Let's write some info to the log
  92. $this->log(
  93. "\r\n------------------------------------------------------------------------------------------------------\r\n" .
  94. 'Domain check failed: website domain (' . $domain . ") is not in allowed domains list.\r\n" .
  95. "SMTP settings won't be used.\r\n" .
  96. "------------------------------------------------------------------------------------------------------\r\n\r\n"
  97. );
  98. }
  99. return;
  100. }
  101. /* Set the mailer type as per config above, this overrides the already called isMail method */
  102. $phpmailer->IsSMTP();
  103. if ( isset( $this->opts['force_from_name_replace'] ) && 1 === $this->opts['force_from_name_replace'] ) {
  104. $from_name = $this->opts['from_name_field'];
  105. } else {
  106. $from_name = ! empty( $phpmailer->FromName ) ? $phpmailer->FromName : $this->opts['from_name_field'];
  107. }
  108. $from_email = $this->opts['from_email_field'];
  109. //set ReplyTo option if needed
  110. //this should be set before SetFrom, otherwise might be ignored
  111. if ( ! empty( $this->opts['reply_to_email'] ) ) {
  112. if ( isset( $this->opts['sub_mode'] ) && 1 === $this->opts['sub_mode'] ) {
  113. if ( count( $phpmailer->getReplyToAddresses() ) >= 1 ) {
  114. // Substitute from_email_field with reply_to_email
  115. if ( array_key_exists( $this->opts['from_email_field'], $phpmailer->getReplyToAddresses() ) ) {
  116. $reply_to_emails = $phpmailer->getReplyToAddresses();
  117. unset( $reply_to_emails[ $this->opts['from_email_field'] ] );
  118. $phpmailer->clearReplyTos();
  119. foreach ( $reply_to_emails as $reply_to_email => $reply_to_name ) {
  120. $phpmailer->AddReplyTo( $reply_to_email, $reply_to_name );
  121. }
  122. $phpmailer->AddReplyTo( $this->opts['reply_to_email'], $from_name );
  123. }
  124. } else { // Reply-to array is empty so add reply_to_email
  125. $phpmailer->AddReplyTo( $this->opts['reply_to_email'], $from_name );
  126. }
  127. } else { // Default behaviour
  128. $phpmailer->AddReplyTo( $this->opts['reply_to_email'], $from_name );
  129. }
  130. }
  131. if ( ! empty( $this->opts['bcc_email'] ) ) {
  132. $bcc_emails = explode( ',', $this->opts['bcc_email'] );
  133. foreach ( $bcc_emails as $bcc_email ) {
  134. $bcc_email = trim( $bcc_email );
  135. $phpmailer->AddBcc( $bcc_email );
  136. }
  137. }
  138. // let's see if we have email ignore list populated
  139. if ( isset( $this->opts['email_ignore_list'] ) && ! empty( $this->opts['email_ignore_list'] ) ) {
  140. $emails_arr = explode( ',', $this->opts['email_ignore_list'] );
  141. $from = $phpmailer->From;
  142. $match_found = false;
  143. foreach ( $emails_arr as $email ) {
  144. if ( strtolower( trim( $email ) ) === strtolower( trim( $from ) ) ) {
  145. $match_found = true;
  146. break;
  147. }
  148. }
  149. if ( $match_found ) {
  150. //we should not override From and Fromname
  151. $from_email = $phpmailer->From;
  152. $from_name = $phpmailer->FromName;
  153. }
  154. }
  155. $phpmailer->From = $from_email;
  156. $phpmailer->FromName = $from_name;
  157. $phpmailer->SetFrom( $phpmailer->From, $phpmailer->FromName );
  158. //This should set Return-Path header for servers that are not properly handling it, but needs testing first
  159. //$phpmailer->Sender = $phpmailer->From;
  160. /* Set the SMTPSecure value */
  161. if ( 'none' !== $this->opts['smtp_settings']['type_encryption'] ) {
  162. $phpmailer->SMTPSecure = $this->opts['smtp_settings']['type_encryption'];
  163. }
  164. /* Set the other options */
  165. $phpmailer->Host = $this->opts['smtp_settings']['host'];
  166. $phpmailer->Port = $this->opts['smtp_settings']['port'];
  167. /* If we're using smtp auth, set the username & password */
  168. if ( 'yes' === $this->opts['smtp_settings']['autentication'] ) {
  169. $phpmailer->SMTPAuth = true;
  170. $phpmailer->Username = $this->opts['smtp_settings']['username'];
  171. $phpmailer->Password = $this->get_password();
  172. }
  173. //PHPMailer 5.2.10 introduced this option. However, this might cause issues if the server is advertising TLS with an invalid certificate.
  174. $phpmailer->SMTPAutoTLS = false;
  175. if ( isset( $this->opts['smtp_settings']['insecure_ssl'] ) && false !== $this->opts['smtp_settings']['insecure_ssl'] ) {
  176. // Insecure SSL option enabled
  177. $phpmailer->SMTPOptions = array(
  178. 'ssl' => array(
  179. 'verify_peer' => false,
  180. 'verify_peer_name' => false,
  181. 'allow_self_signed' => true,
  182. ),
  183. );
  184. }
  185. //set reasonable timeout
  186. $phpmailer->Timeout = 10;
  187. }
  188. public function test_mail( $to_email, $subject, $message ) {
  189. $ret = array();
  190. if ( ! $this->credentials_configured() ) {
  191. return false;
  192. }
  193. global $wp_version;
  194. if ( version_compare( $wp_version, '5.4.99' ) > 0 ) {
  195. require_once ABSPATH . WPINC . '/PHPMailer/PHPMailer.php';
  196. require_once ABSPATH . WPINC . '/PHPMailer/SMTP.php';
  197. require_once ABSPATH . WPINC . '/PHPMailer/Exception.php';
  198. $mail = new PHPMailer( true );
  199. } else {
  200. require_once ABSPATH . WPINC . '/class-phpmailer.php';
  201. $mail = new \PHPMailer( true );
  202. }
  203. try {
  204. $charset = get_bloginfo( 'charset' );
  205. $mail->CharSet = $charset;
  206. $from_name = $this->opts['from_name_field'];
  207. $from_email = $this->opts['from_email_field'];
  208. $mail->IsSMTP();
  209. // send plain text test email
  210. $mail->ContentType = 'text/plain';
  211. $mail->IsHTML( false );
  212. /* If using smtp auth, set the username & password */
  213. if ( 'yes' === $this->opts['smtp_settings']['autentication'] ) {
  214. $mail->SMTPAuth = true;
  215. $mail->Username = $this->opts['smtp_settings']['username'];
  216. $mail->Password = $this->get_password();
  217. }
  218. /* Set the SMTPSecure value, if set to none, leave this blank */
  219. if ( 'none' !== $this->opts['smtp_settings']['type_encryption'] ) {
  220. $mail->SMTPSecure = $this->opts['smtp_settings']['type_encryption'];
  221. }
  222. /* PHPMailer 5.2.10 introduced this option. However, this might cause issues if the server is advertising TLS with an invalid certificate. */
  223. $mail->SMTPAutoTLS = false;
  224. if ( isset( $this->opts['smtp_settings']['insecure_ssl'] ) && false !== $this->opts['smtp_settings']['insecure_ssl'] ) {
  225. // Insecure SSL option enabled
  226. $mail->SMTPOptions = array(
  227. 'ssl' => array(
  228. 'verify_peer' => false,
  229. 'verify_peer_name' => false,
  230. 'allow_self_signed' => true,
  231. ),
  232. );
  233. }
  234. /* Set the other options */
  235. $mail->Host = $this->opts['smtp_settings']['host'];
  236. $mail->Port = $this->opts['smtp_settings']['port'];
  237. //Add reply-to if set in settings.
  238. if ( ! empty( $this->opts['reply_to_email'] ) ) {
  239. $mail->AddReplyTo( $this->opts['reply_to_email'], $from_name );
  240. }
  241. //Add BCC if set in settings.
  242. if ( ! empty( $this->opts['bcc_email'] ) ) {
  243. $bcc_emails = explode( ',', $this->opts['bcc_email'] );
  244. foreach ( $bcc_emails as $bcc_email ) {
  245. $bcc_email = trim( $bcc_email );
  246. $mail->AddBcc( $bcc_email );
  247. }
  248. }
  249. $mail->SetFrom( $from_email, $from_name );
  250. //This should set Return-Path header for servers that are not properly handling it, but needs testing first
  251. //$mail->Sender = $mail->From;
  252. $mail->Subject = $subject;
  253. $mail->Body = $message;
  254. $mail->AddAddress( $to_email );
  255. global $debug_msg;
  256. $debug_msg = '';
  257. $mail->Debugoutput = function ( $str, $level ) {
  258. global $debug_msg;
  259. $debug_msg .= $str;
  260. };
  261. $mail->SMTPDebug = 1;
  262. //set reasonable timeout
  263. $mail->Timeout = 10;
  264. /* Send mail and return result */
  265. $mail->Send();
  266. $mail->ClearAddresses();
  267. $mail->ClearAllRecipients();
  268. } catch ( \Exception $e ) {
  269. $ret['error'] = $mail->ErrorInfo;
  270. } catch ( \Throwable $e ) {
  271. $ret['error'] = $mail->ErrorInfo;
  272. }
  273. $ret['debug_log'] = $debug_msg;
  274. return $ret;
  275. }
  276. public function admin_init() {
  277. if ( current_user_can( 'manage_options' ) ) {
  278. if ( defined( 'DOING_AJAX' ) && DOING_AJAX ) {
  279. add_action( 'wp_ajax_swpsmtp_clear_log', array( $this, 'clear_log' ) );
  280. add_action( 'wp_ajax_swpsmtp_self_destruct', array( $this, 'self_destruct_handler' ) );
  281. }
  282. //view log file
  283. if ( isset( $_GET['swpsmtp_action'] ) ) {
  284. if ( 'view_log' === $_GET['swpsmtp_action'] ) {
  285. $log_file_name = isset( $this->opts['smtp_settings']['log_file_name'] ) ? $this->opts['smtp_settings']['log_file_name'] : '';
  286. if ( empty( $log_file_name ) ) {
  287. //Nothing in the log file yet so nothing to show.
  288. wp_die( 'Nothing in the log file yet.' );
  289. }
  290. if ( ! file_exists( plugin_dir_path( __FILE__ ) . $log_file_name ) ) {
  291. if ( $this->log( self::$reset_log_str ) === false ) {
  292. wp_die( esc_html( sprintf( 'Can\'t write to log file. Check if plugin directory (%s) is writeable.', plugin_dir_path( __FILE__ ) ) ) );
  293. };
  294. }
  295. $logfile = fopen( plugin_dir_path( __FILE__ ) . $log_file_name, 'rb' ); //phpcs:ignore
  296. if ( ! $logfile ) {
  297. wp_die( 'Can\'t open log file.' );
  298. }
  299. header( 'Content-Type: text/plain' );
  300. fpassthru( $logfile );
  301. die;
  302. }
  303. }
  304. //check if this is export settings request
  305. $is_export_settings = filter_input( INPUT_POST, 'swpsmtp_export_settings', FILTER_SANITIZE_NUMBER_INT );
  306. if ( $is_export_settings ) {
  307. check_admin_referer( 'easy_wp_smtp_export_settings', 'easy_wp_smtp_export_settings_nonce' );
  308. $data = array();
  309. $opts = get_option( 'swpsmtp_options', array() );
  310. $data['swpsmtp_options'] = $opts;
  311. $swpsmtp_pass_encrypted = get_option( 'swpsmtp_pass_encrypted', false );
  312. $data['swpsmtp_pass_encrypted'] = $swpsmtp_pass_encrypted;
  313. if ( $swpsmtp_pass_encrypted ) {
  314. $swpsmtp_enc_key = get_option( 'swpsmtp_enc_key', false );
  315. $data['swpsmtp_enc_key'] = $swpsmtp_enc_key;
  316. }
  317. $smtp_test_mail = get_option( 'smtp_test_mail', array() );
  318. $data['smtp_test_mail'] = $smtp_test_mail;
  319. $out = array();
  320. $out['data'] = wp_json_encode( $data );
  321. $out['ver'] = 2;
  322. $out['checksum'] = md5( $out['data'] );
  323. $filename = 'easy_wp_smtp_settings.json';
  324. header( 'Content-Disposition: attachment; filename="' . $filename . '"' );
  325. header( 'Content-Type: application/json' );
  326. echo wp_json_encode( $out );
  327. exit;
  328. }
  329. $is_import_settings = filter_input( INPUT_POST, 'swpsmtp_import_settings', FILTER_SANITIZE_NUMBER_INT );
  330. if ( $is_import_settings ) {
  331. check_admin_referer( 'easy_wp_smtp_import_settings', 'easy_wp_smtp_import_settings_nonce' );
  332. $err_msg = __( 'Error occurred during settings import', 'easy-wp-smtp' );
  333. if ( empty( $_FILES['swpsmtp_import_settings_file'] ) ) {
  334. echo esc_html( $err_msg );
  335. wp_die();
  336. }
  337. $in_raw = file_get_contents( $_FILES['swpsmtp_import_settings_file']['tmp_name'] ); //phpcs:ignore
  338. try {
  339. $in = json_decode( $in_raw, true );
  340. if ( json_last_error() !== 0 ) {
  341. $in = unserialize( $in_raw ); //phpcs:ignore
  342. }
  343. if ( empty( $in['data'] ) ) {
  344. echo esc_html( $err_msg );
  345. wp_die();
  346. }
  347. if ( empty( $in['checksum'] ) ) {
  348. echo esc_html( $err_msg );
  349. wp_die();
  350. }
  351. if ( md5( $in['data'] ) !== $in['checksum'] ) {
  352. echo esc_html( $err_msg );
  353. wp_die();
  354. }
  355. $data = json_decode( $in['data'], true );
  356. if ( json_last_error() !== 0 ) {
  357. $data = unserialize( $in['data'] ); //phpcs:ignore
  358. }
  359. update_option( 'swpsmtp_options', $data['swpsmtp_options'] );
  360. update_option( 'swpsmtp_pass_encrypted', $data['swpsmtp_pass_encrypted'] );
  361. if ( $data['swpsmtp_pass_encrypted'] ) {
  362. update_option( 'swpsmtp_enc_key', $data['swpsmtp_enc_key'] );
  363. }
  364. update_option( 'smtp_test_mail', $data['smtp_test_mail'] );
  365. set_transient( 'easy_wp_smtp_settings_import_success', true, 60 * 60 );
  366. $url = admin_url() . 'options-general.php?page=swpsmtp_settings';
  367. wp_safe_redirect( $url );
  368. exit;
  369. } catch ( Exception $ex ) {
  370. echo esc_html( $err_msg );
  371. wp_die();
  372. }
  373. }
  374. }
  375. }
  376. public function admin_notices() {
  377. if ( ! $this->credentials_configured() ) {
  378. $settings_url = admin_url() . 'options-general.php?page=swpsmtp_settings';
  379. ?>
  380. <div class="error">
  381. <p>
  382. <?php
  383. printf( __( 'Please configure your SMTP credentials in the <a href="%s">settings menu</a> in order to send email using Easy WP SMTP plugin.', 'easy-wp-smtp' ), esc_url( $settings_url ) );
  384. ?>
  385. </p>
  386. </div>
  387. <?php
  388. }
  389. $settings_import_notice = get_transient( 'easy_wp_smtp_settings_import_success' );
  390. if ( $settings_import_notice ) {
  391. delete_transient( 'easy_wp_smtp_settings_import_success' );
  392. ?>
  393. <div class="updated">
  394. <p><?php echo esc_html( __( 'Settings have been imported successfully.', 'easy-wp-smtp' ) ); ?></p>
  395. </div>
  396. <?php
  397. }
  398. }
  399. public function get_log_file_path() {
  400. $log_file_name = 'logs' . DIRECTORY_SEPARATOR . '.' . uniqid( '', true ) . '.txt';
  401. $log_file_name = apply_filters( 'swpsmtp_log_file_path_override', $log_file_name );
  402. return $log_file_name;
  403. }
  404. public function clear_log() {
  405. if ( ! check_ajax_referer( 'easy-wp-smtp-clear-log', 'nonce', false ) ) {
  406. echo esc_html( __( 'Nonce check failed.', 'easy-wp-smtp' ) );
  407. exit;
  408. };
  409. if ( $this->log( self::$reset_log_str, true ) !== false ) {
  410. echo '1';
  411. } else {
  412. echo esc_html( __( "Can't clear log - file is not writeable.", 'easy-wp-smtp' ) );
  413. }
  414. die;
  415. }
  416. public function log( $str, $overwrite = false ) {
  417. try {
  418. $log_file_name = '';
  419. if ( isset( $this->opts['smtp_settings']['log_file_name'] ) ) {
  420. $log_file_name = $this->opts['smtp_settings']['log_file_name'];
  421. }
  422. if ( empty( $log_file_name ) || $overwrite ) {
  423. if ( ! empty( $log_file_name ) && file_exists( plugin_dir_path( __FILE__ ) . $log_file_name ) ) {
  424. unlink( plugin_dir_path( __FILE__ ) . $log_file_name );
  425. }
  426. $log_file_name = $this->get_log_file_path();
  427. $this->opts['smtp_settings']['log_file_name'] = $log_file_name;
  428. update_option( 'swpsmtp_options', $this->opts );
  429. file_put_contents( plugin_dir_path( __FILE__ ) . $log_file_name, self::$reset_log_str ); //phpcs:ignore
  430. }
  431. //Timestamp the log output
  432. $str = '[' . date( 'm/d/Y g:i:s A' ) . '] - ' . $str;
  433. //Write to the log file
  434. return ( file_put_contents( plugin_dir_path( __FILE__ ) . $log_file_name, $str, ( ! $overwrite ? FILE_APPEND : 0 ) ) ); //phpcs:ignore
  435. } catch ( \Exception $e ) {
  436. return false;
  437. }
  438. }
  439. public function plugin_action_links( $links, $file ) {
  440. if ( plugin_basename( $this->plugin_file ) === $file ) {
  441. $settings_link = '<a href="options-general.php?page=swpsmtp_settings">' . __( 'Settings', 'easy-wp-smtp' ) . '</a>';
  442. array_unshift( $links, $settings_link );
  443. }
  444. return $links;
  445. }
  446. public function register_plugin_links( $links, $file ) {
  447. if ( plugin_basename( $this->plugin_file ) === $file ) {
  448. $links[] = '<a href="options-general.php?page=swpsmtp_settings">' . __( 'Settings', 'easy-wp-smtp' ) . '</a>';
  449. }
  450. return $links;
  451. }
  452. public function plugins_loaded_handler() {
  453. load_plugin_textdomain( 'easy-wp-smtp', false, dirname( plugin_basename( $this->plugin_file ) ) . '/languages/' );
  454. }
  455. public function is_domain_blocked() {
  456. //check if Domain Check enabled
  457. if ( isset( $this->opts['enable_domain_check'] ) && $this->opts['enable_domain_check'] ) {
  458. //check if allowed domains list is not blank
  459. if ( isset( $this->opts['allowed_domains'] ) && ! empty( $this->opts['allowed_domains'] ) ) {
  460. $this->opts['allowed_domains'] = EasyWPSMTP_Utils::base64_decode_maybe( $this->opts['allowed_domains'] );
  461. //let's see if we have one domain or coma-separated domains
  462. $domains_arr = explode( ',', $this->opts['allowed_domains'] );
  463. //TODO: Change parse_url() to wp_parse_url() and bump required WP version to 4.4.0
  464. $site_domain = parse_url( get_site_url(), PHP_URL_HOST ); //phpcs:ignore
  465. $match_found = false;
  466. foreach ( $domains_arr as $domain ) {
  467. if ( strtolower( trim( $domain ) ) === strtolower( trim( $site_domain ) ) ) {
  468. $match_found = true;
  469. break;
  470. }
  471. }
  472. if ( ! $match_found ) {
  473. return $site_domain;
  474. }
  475. }
  476. }
  477. return false;
  478. }
  479. public function get_password() {
  480. $temp_password = isset( $this->opts['smtp_settings']['password'] ) ? $this->opts['smtp_settings']['password'] : '';
  481. if ( '' === $temp_password ) {
  482. return '';
  483. }
  484. try {
  485. if ( get_option( 'swpsmtp_pass_encrypted' ) ) {
  486. //this is encrypted password
  487. $cryptor = EasyWPSMTP_Utils::get_instance();
  488. $decrypted = $cryptor->decrypt_password( $temp_password );
  489. //check if encryption option is disabled
  490. if ( empty( $this->opts['smtp_settings']['encrypt_pass'] ) ) {
  491. //it is. let's save decrypted password
  492. $this->opts['smtp_settings']['password'] = $this->encrypt_password( addslashes( $decrypted ) );
  493. update_option( 'swpsmtp_options', $this->opts );
  494. }
  495. return $decrypted;
  496. }
  497. } catch ( Exception $e ) {
  498. $this->log( $e->getMessage() );
  499. return '';
  500. }
  501. $password = '';
  502. $decoded_pass = base64_decode( $temp_password ); //phpcs:ignore
  503. /* no additional checks for servers that aren't configured with mbstring enabled */
  504. if ( ! function_exists( 'mb_detect_encoding' ) ) {
  505. return $decoded_pass;
  506. }
  507. /* end of mbstring check */
  508. if ( base64_encode( $decoded_pass ) === $temp_password ) { //phpcs:ignore
  509. //it might be encoded
  510. if ( false === mb_detect_encoding( $decoded_pass ) ) { //could not find character encoding.
  511. $password = $temp_password;
  512. } else {
  513. $password = base64_decode( $temp_password ); //phpcs:ignore
  514. }
  515. } else { //not encoded
  516. $password = $temp_password;
  517. }
  518. return stripslashes( $password );
  519. }
  520. public function encrypt_password( $pass ) {
  521. if ( '' === $pass ) {
  522. return '';
  523. }
  524. if ( empty( $this->opts['smtp_settings']['encrypt_pass'] ) || ! extension_loaded( 'openssl' ) ) {
  525. // no openssl extension loaded - we can't encrypt the password
  526. $password = base64_encode( $pass ); //phpcs:ignore
  527. update_option( 'swpsmtp_pass_encrypted', false );
  528. } else {
  529. // let's encrypt password
  530. $cryptor = EasyWPSMTP_Utils::get_instance();
  531. $password = $cryptor->encrypt_password( $pass );
  532. update_option( 'swpsmtp_pass_encrypted', true );
  533. }
  534. return $password;
  535. }
  536. public function credentials_configured() {
  537. $credentials_configured = true;
  538. if ( ! isset( $this->opts['from_email_field'] ) || empty( $this->opts['from_email_field'] ) ) {
  539. $credentials_configured = false;
  540. }
  541. if ( ! isset( $this->opts['from_name_field'] ) || empty( $this->opts['from_name_field'] ) ) {
  542. $credentials_configured = false;
  543. }
  544. return $credentials_configured;
  545. }
  546. public function activate() {
  547. $swpsmtp_options_default = array(
  548. 'from_email_field' => '',
  549. 'from_name_field' => '',
  550. 'force_from_name_replace' => 0,
  551. 'sub_mode' => 0,
  552. 'smtp_settings' => array(
  553. 'host' => 'smtp.example.com',
  554. 'type_encryption' => 'none',
  555. 'port' => 25,
  556. 'autentication' => 'yes',
  557. 'username' => '',
  558. 'password' => '',
  559. ),
  560. );
  561. /* install the default plugin options if needed */
  562. if ( empty( $this->opts ) ) {
  563. $this->opts = $swpsmtp_options_default;
  564. }
  565. $this->opts = array_merge( $swpsmtp_options_default, $this->opts );
  566. // reset log file
  567. $this->log( self::$reset_log_str, true );
  568. update_option( 'swpsmtp_options', $this->opts, 'yes' );
  569. //add current domain to allowed domains list
  570. if ( ! isset( $this->opts['allowed_domains'] ) ) {
  571. //TODO: Change parse_url() to wp_parse_url() and bump required WP version to 4.4.0
  572. $domain = parse_url( get_site_url(), PHP_URL_HOST ); //phpcs:ignore
  573. if ( $domain ) {
  574. $this->opts['allowed_domains'] = base64_encode( $domain ); //phpcs:ignore
  575. update_option( 'swpsmtp_options', $this->opts );
  576. }
  577. } else { // let's check if existing value should be base64 encoded
  578. if ( ! empty( $this->opts['allowed_domains'] ) ) {
  579. if ( EasyWPSMTP_Utils::base64_decode_maybe( $this->opts['allowed_domains'] ) === $this->opts['allowed_domains'] ) {
  580. $this->opts['allowed_domains'] = base64_encode( $this->opts['allowed_domains'] ); //phpcs:ignore
  581. update_option( 'swpsmtp_options', $this->opts );
  582. }
  583. }
  584. }
  585. // Encrypt password if needed
  586. if ( ! get_option( 'swpsmtp_pass_encrypted' ) ) {
  587. if ( extension_loaded( 'openssl' ) ) {
  588. if ( '' !== $this->opts['smtp_settings']['password'] ) {
  589. $this->opts['smtp_settings']['password'] = $this->encrypt_password( $this->get_password() );
  590. update_option( 'swpsmtp_options', $this->opts );
  591. }
  592. }
  593. }
  594. }
  595. public function deactivate() {
  596. // reset log file
  597. $this->log( self::$reset_log_str, true );
  598. }
  599. public function self_destruct_handler() {
  600. $err_msg = __( 'Please refresh the page and try again.', 'easy-wp-smtp' );
  601. $trans = get_transient( 'easy_wp_smtp_sd_code' );
  602. if ( empty( $trans ) ) {
  603. echo esc_html( $err_msg );
  604. exit;
  605. }
  606. $sd_code = filter_input( INPUT_POST, 'sd_code', FILTER_SANITIZE_STRING );
  607. if ( $trans !== $sd_code ) {
  608. echo esc_html( $err_msg );
  609. exit;
  610. }
  611. $this->log( self::$reset_log_str, true );
  612. delete_site_option( 'swpsmtp_options' );
  613. delete_option( 'swpsmtp_options' );
  614. delete_site_option( 'smtp_test_mail' );
  615. delete_option( 'smtp_test_mail' );
  616. delete_site_option( 'swpsmtp_pass_encrypted' );
  617. delete_option( 'swpsmtp_pass_encrypted' );
  618. delete_option( 'swpsmtp_enc_key' );
  619. echo 1;
  620. deactivate_plugins( __FILE__ );
  621. exit;
  622. }
  623. }
  624. EasyWPSMTP::get_instance();
  625. function swpsmtp_uninstall() {
  626. // Don't delete plugin options. It is better to retain the options so if someone accidentally deactivates, the configuration is not lost.
  627. //delete_site_option('swpsmtp_options');
  628. //delete_option('swpsmtp_options');
  629. }