Version Description
- Revival
- Added feedback system
- Updated readme
Download this release
Release Info
Developer | cl272 |
Plugin | Enhanced Text Widget |
Version | 1.4.7 |
Comparing to | |
See all releases |
Code changes from version 1.4.6 to 1.4.7
- LICENSE.md +0 -7
- README.md +0 -98
- analyst/assets/css/customize.css +280 -0
- analyst/assets/img/pencil.png +0 -0
- analyst/assets/img/shield_question.png +0 -0
- analyst/assets/img/shield_success.png +0 -0
- analyst/assets/img/smile.png +0 -0
- analyst/assets/index.php +2 -0
- analyst/assets/js/customize.js +29 -0
- analyst/autoload.php +40 -0
- analyst/index.php +2 -0
- analyst/main.php +36 -0
- analyst/sdk_resolver.php +79 -0
- analyst/src/Account/Account.php +584 -0
- analyst/src/Account/AccountData.php +176 -0
- analyst/src/Account/AccountDataFactory.php +125 -0
- analyst/src/Analyst.php +167 -0
- analyst/src/ApiRequestor.php +257 -0
- analyst/src/ApiResponse.php +44 -0
- analyst/src/Cache/DatabaseCache.php +127 -0
- analyst/src/Collector.php +217 -0
- analyst/src/Contracts/AnalystContract.php +12 -0
- analyst/src/Contracts/CacheContract.php +47 -0
- analyst/src/Contracts/HttpClientContract.php +25 -0
- analyst/src/Contracts/RequestContract.php +22 -0
- analyst/src/Contracts/RequestorContract.php +44 -0
- analyst/src/Contracts/TrackerContract.php +69 -0
- analyst/src/Core/AbstractFactory.php +27 -0
- analyst/src/Http/CurlHttpClient.php +102 -0
- analyst/src/Http/DummyHttpClient.php +33 -0
- analyst/src/Http/Requests/AbstractLoggerRequest.php +64 -0
- analyst/src/Http/Requests/ActivateRequest.php +42 -0
- analyst/src/Http/Requests/DeactivateRequest.php +64 -0
- analyst/src/Http/Requests/InstallRequest.php +38 -0
- analyst/src/Http/Requests/OptInRequest.php +42 -0
- analyst/src/Http/Requests/OptOutRequest.php +40 -0
- analyst/src/Http/Requests/UninstallRequest.php +36 -0
- analyst/src/Http/WordPressHttpClient.php +61 -0
- analyst/src/Mutator.php +103 -0
- analyst/src/Notices/Notice.php +121 -0
- analyst/src/Notices/NoticeFactory.php +130 -0
- analyst/src/helpers.php +84 -0
- analyst/templates/forms/deactivate.php +156 -0
- analyst/templates/forms/install.php +113 -0
- analyst/templates/notice.php +10 -0
- analyst/templates/optin.php +60 -0
- analyst/templates/optout.php +109 -0
- analyst/version.php +15 -0
- enhanced-text-widget.php +11 -3
- readme.txt +13 -12
LICENSE.md
DELETED
@@ -1,7 +0,0 @@
|
|
1 |
-
Copyright (c) Boston Dell-Vandenberg
|
2 |
-
|
3 |
-
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
4 |
-
|
5 |
-
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
6 |
-
|
7 |
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
README.md
DELETED
@@ -1,98 +0,0 @@
|
|
1 |
-
# Enhanced Text Widget
|
2 |
-
|
3 |
-
An enhanced version of the default text widget where you may have Text, HTML, CSS, JavaScript, Flash, Shortcodes and/or PHP as content with linkable widget title.
|
4 |
-
|
5 |
-
## Options
|
6 |
-
|
7 |
-
* Title
|
8 |
-
* Title URL
|
9 |
-
* Widget CSS class
|
10 |
-
* Content supports Text, HTML, CSS, JavaScript, Flash, Shortcodes, and PHP
|
11 |
-
* Option to not display a title
|
12 |
-
* Option to open Title URL in new window
|
13 |
-
* Option to automatically add paragraphs to content
|
14 |
-
* Option to not output before/after_widget/title (bare widget)
|
15 |
-
|
16 |
-
## More Information
|
17 |
-
|
18 |
-
* For help use [wordpress.org](http://wordpress.org/support/plugin/enhanced-text-widget/)
|
19 |
-
* Fork or contribute on [Github](https://github.com/bostondv/enhanced-text-widget/)
|
20 |
-
* Follow me on [Twitter](http://twitter.com/bostondv/)
|
21 |
-
* View my other [WordPress Plugins](http://profiles.wordpress.org/bostondv/)
|
22 |
-
|
23 |
-
## Support
|
24 |
-
|
25 |
-
Enjoy this plugin? [Send a tip to support development](http://bostondv.com/tips/).
|
26 |
-
|
27 |
-
## Installation
|
28 |
-
|
29 |
-
1. Download and extract the zip archive
|
30 |
-
2. Upload `enhanced-text-widget` folder to `/wp-content/plugins/`
|
31 |
-
3. Activate the plugin through the 'Plugins' menu in WordPress
|
32 |
-
4. Add the widget to a sidebar and configure the options as desired
|
33 |
-
|
34 |
-
## Upgrade Notice
|
35 |
-
|
36 |
-
### 1.3.4
|
37 |
-
This adds option to hide the title
|
38 |
-
|
39 |
-
### 1.2
|
40 |
-
This adds option to display bare text (no before/after widget/title elements are shown).
|
41 |
-
|
42 |
-
### 1.1
|
43 |
-
This adds a CSS class parameter to each widget.
|
44 |
-
|
45 |
-
### 1.0
|
46 |
-
This is the initial release.
|
47 |
-
|
48 |
-
## Changelog ==
|
49 |
-
|
50 |
-
### 1.4.6
|
51 |
-
* Tested up to WordPress 4.8
|
52 |
-
|
53 |
-
### 1.4.5
|
54 |
-
* Adds option to hide empty widgets
|
55 |
-
|
56 |
-
### 1.4.4
|
57 |
-
* Updates tested to 4.0
|
58 |
-
* Updates license to GPLv3
|
59 |
-
* Minor code refactoring
|
60 |
-
|
61 |
-
### 1.4.3
|
62 |
-
* Adds Italian translation
|
63 |
-
|
64 |
-
### 1.4.2
|
65 |
-
* Removes widget caching
|
66 |
-
* Fixes issue with multiline PHP code
|
67 |
-
|
68 |
-
### 1.4
|
69 |
-
* Display text field in monospace font
|
70 |
-
* Code refactoring
|
71 |
-
|
72 |
-
### 1.3.4
|
73 |
-
* This adds option to hide the title
|
74 |
-
* Tested up to 3.5.1
|
75 |
-
|
76 |
-
### 1.3.3
|
77 |
-
* Fixes "Do not output before/after_widget/title" option
|
78 |
-
* Code cleanup
|
79 |
-
|
80 |
-
### 1.3.2
|
81 |
-
* Bug fixes
|
82 |
-
|
83 |
-
### 1.3
|
84 |
-
* Fixed debug warnings
|
85 |
-
* Fixed code merge issue
|
86 |
-
|
87 |
-
### 1.2
|
88 |
-
* This adds option to display bare text (no before/after widget/title elements are shown).
|
89 |
-
|
90 |
-
### 1.1
|
91 |
-
* Adds css class parameter that wraps widget.
|
92 |
-
|
93 |
-
### 1.0
|
94 |
-
* First release.
|
95 |
-
|
96 |
-
## About
|
97 |
-
|
98 |
-
Made with <3 by [Boston Dell-Vandenberg](http://bostondv.com/).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
analyst/assets/css/customize.css
ADDED
@@ -0,0 +1,280 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.analyst-action-opt {
|
2 |
+
cursor: pointer;
|
3 |
+
}
|
4 |
+
|
5 |
+
.analyst-modal {
|
6 |
+
color: #000000;
|
7 |
+
display: none;
|
8 |
+
position: fixed;
|
9 |
+
z-index: 1000;
|
10 |
+
padding-top: 100px;
|
11 |
+
left: 0;
|
12 |
+
top: 0;
|
13 |
+
width: 100%;
|
14 |
+
height: 100%;
|
15 |
+
overflow: auto;
|
16 |
+
background-color: rgb(0,0,0);
|
17 |
+
background-color: rgba(0,0,0,0.4);
|
18 |
+
}
|
19 |
+
|
20 |
+
.analyst-modal-content {
|
21 |
+
font-family: Helvetica, serif;
|
22 |
+
position: relative;
|
23 |
+
background-color: #fefefe;
|
24 |
+
margin: auto;
|
25 |
+
padding: 35px 35px 20px;
|
26 |
+
border: 1px solid #F2F2F2;
|
27 |
+
width: 40%;
|
28 |
+
box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19);
|
29 |
+
-webkit-animation-name: analyst-animatetop;
|
30 |
+
-webkit-animation-duration: 0.4s;
|
31 |
+
animation-name: analyst-animatetop;
|
32 |
+
animation-duration: 0.4s
|
33 |
+
}
|
34 |
+
|
35 |
+
.analyst-btn-success {
|
36 |
+
cursor: pointer;
|
37 |
+
color: #ffffff;
|
38 |
+
background-color: #00AF5E;
|
39 |
+
border: none;
|
40 |
+
width: 100%;
|
41 |
+
font-size: 18px;
|
42 |
+
padding: 8px;
|
43 |
+
font-weight: bold;
|
44 |
+
}
|
45 |
+
|
46 |
+
.analyst-btn-grey {
|
47 |
+
cursor: pointer;
|
48 |
+
color: #2D2D2D;
|
49 |
+
background-color: #D8D8D8;
|
50 |
+
border: none;
|
51 |
+
width: 100%;
|
52 |
+
font-size: 18px;
|
53 |
+
padding: 8px;
|
54 |
+
font-weight: bold;
|
55 |
+
}
|
56 |
+
|
57 |
+
.analyst-btn-secondary-ghost {
|
58 |
+
cursor: pointer;
|
59 |
+
background: transparent;
|
60 |
+
border: none;
|
61 |
+
color: #898686;
|
62 |
+
font-size: 18px;
|
63 |
+
}
|
64 |
+
|
65 |
+
.analyst-modal-def-top-padding {
|
66 |
+
padding-top: 20px;
|
67 |
+
}
|
68 |
+
|
69 |
+
.analyst-modal-header {
|
70 |
+
font-size: 20px;
|
71 |
+
font-weight: bold;
|
72 |
+
}
|
73 |
+
|
74 |
+
/*INSTALL STYLES*/
|
75 |
+
.analyst-install-footer {
|
76 |
+
padding-top: 10px;
|
77 |
+
text-align: center;
|
78 |
+
}
|
79 |
+
|
80 |
+
.analyst-install-image-block {
|
81 |
+
width: 140px;
|
82 |
+
}
|
83 |
+
|
84 |
+
.analyst-install-image-block img {
|
85 |
+
width: inherit;
|
86 |
+
}
|
87 |
+
|
88 |
+
.analyst-install-description-block {
|
89 |
+
padding-left: 40px;
|
90 |
+
padding-top: 5px
|
91 |
+
}
|
92 |
+
|
93 |
+
.analyst-install-description-text {
|
94 |
+
font-size: 16px;
|
95 |
+
color: #000000;
|
96 |
+
}
|
97 |
+
|
98 |
+
.analyst-install-permissions-list {
|
99 |
+
list-style: disc inside;
|
100 |
+
}
|
101 |
+
|
102 |
+
.analyst-install-permissions-list li {
|
103 |
+
padding-left: 15px;
|
104 |
+
margin-bottom: 2px;
|
105 |
+
}
|
106 |
+
|
107 |
+
.analyst-install-footer span {
|
108 |
+
color: #8a8787;
|
109 |
+
padding-right: 10px;
|
110 |
+
padding-left: 10px;
|
111 |
+
}
|
112 |
+
|
113 |
+
.analyst-install-footer span:not(:last-child) {
|
114 |
+
border-right: 1px solid #8a8787;
|
115 |
+
}
|
116 |
+
|
117 |
+
/*INSTALL STYLES*/
|
118 |
+
|
119 |
+
.reason-answer {
|
120 |
+
padding: 7px;
|
121 |
+
margin-left: 23px;
|
122 |
+
border: 1px solid #F2F2F2;
|
123 |
+
}
|
124 |
+
|
125 |
+
.analyst-link {
|
126 |
+
color: #00AF5E;
|
127 |
+
text-decoration: none;
|
128 |
+
}
|
129 |
+
|
130 |
+
.analyst-action-text {
|
131 |
+
cursor: pointer;
|
132 |
+
}
|
133 |
+
|
134 |
+
.analyst-action-text:hover {
|
135 |
+
color: #9d9a9a;
|
136 |
+
}
|
137 |
+
|
138 |
+
.analyst-disable-modal-mask {
|
139 |
+
width: 100%;
|
140 |
+
height: 100%;
|
141 |
+
opacity: 0.5;
|
142 |
+
position: absolute;
|
143 |
+
background: white;
|
144 |
+
top: 0;
|
145 |
+
left: 0;
|
146 |
+
}
|
147 |
+
|
148 |
+
.analyst-smile-image {
|
149 |
+
vertical-align: middle;
|
150 |
+
padding-bottom: 3px;
|
151 |
+
width: 24px;
|
152 |
+
}
|
153 |
+
|
154 |
+
#analyst-deactivation-reasons li {
|
155 |
+
padding-bottom: 3px;
|
156 |
+
font-size: 16px;
|
157 |
+
color: #000000;
|
158 |
+
}
|
159 |
+
|
160 |
+
@-webkit-keyframes analyst-animatetop {
|
161 |
+
from {top:-300px; opacity:0}
|
162 |
+
to {top:0; opacity:1}
|
163 |
+
}
|
164 |
+
|
165 |
+
@keyframes analyst-animatetop {
|
166 |
+
from {top:-300px; opacity:0}
|
167 |
+
to {top:0; opacity:1}
|
168 |
+
}
|
169 |
+
|
170 |
+
.analyst-modal-close {
|
171 |
+
color: #48036F;
|
172 |
+
font-size: 28px;
|
173 |
+
font-weight: bold;
|
174 |
+
top: 12px;
|
175 |
+
position: absolute;
|
176 |
+
right: 15px;
|
177 |
+
}
|
178 |
+
|
179 |
+
.analyst-modal-close:hover,
|
180 |
+
.analyst-modal-close:focus {
|
181 |
+
color: #000;
|
182 |
+
text-decoration: none;
|
183 |
+
cursor: pointer;
|
184 |
+
}
|
185 |
+
|
186 |
+
.analyst-modal-body {padding: 2px 16px;}
|
187 |
+
|
188 |
+
.analyst-modal-footer {
|
189 |
+
padding: 6px 16px;
|
190 |
+
background-color: #FFE773;
|
191 |
+
color: white;
|
192 |
+
}
|
193 |
+
|
194 |
+
#analyst-deactivate-modal .question-answer input, textarea {
|
195 |
+
margin-top: 5px;
|
196 |
+
width: 100%;
|
197 |
+
}
|
198 |
+
|
199 |
+
.analyst-btn-primary {
|
200 |
+
cursor: pointer;
|
201 |
+
border: none;
|
202 |
+
display:inline-block;
|
203 |
+
padding:0.7em 1.4em;
|
204 |
+
margin:0 0.3em 0.3em 0;
|
205 |
+
border-radius:0.15em;
|
206 |
+
box-sizing: border-box;
|
207 |
+
text-decoration:none;
|
208 |
+
font-family:'Roboto',sans-serif;
|
209 |
+
text-transform:uppercase;
|
210 |
+
font-weight:400;
|
211 |
+
color:#FFFFFF;
|
212 |
+
background-color:#9F3ED5;
|
213 |
+
box-shadow:inset 0 -0.6em 0 -0.35em rgba(0,0,0,0.17);
|
214 |
+
text-align:center;
|
215 |
+
position:relative;
|
216 |
+
}
|
217 |
+
|
218 |
+
.analyst-btn-primary:disabled {
|
219 |
+
background-color: #AD66D5;
|
220 |
+
cursor: not-allowed;
|
221 |
+
}
|
222 |
+
|
223 |
+
.analyst-btn-primary:active{
|
224 |
+
top:0.1em;
|
225 |
+
}
|
226 |
+
@media all and (max-width:30em){
|
227 |
+
.analyst-btn-primary {
|
228 |
+
display:block;
|
229 |
+
margin:0.4em auto;
|
230 |
+
}
|
231 |
+
}
|
232 |
+
|
233 |
+
.analyst-btn-secondary {
|
234 |
+
cursor: pointer;
|
235 |
+
border: none;
|
236 |
+
display:inline-block;
|
237 |
+
padding:0.7em 1.4em;
|
238 |
+
margin:0 0.3em 0.3em 0;
|
239 |
+
border-radius:0.15em;
|
240 |
+
box-sizing: border-box;
|
241 |
+
text-decoration:none;
|
242 |
+
font-family:'Roboto',sans-serif;
|
243 |
+
text-transform:uppercase;
|
244 |
+
font-weight:400;
|
245 |
+
color:#FFFFFF;
|
246 |
+
background-color:#6C8CD5;
|
247 |
+
box-shadow:inset 0 -0.6em 0 -0.35em rgba(0,0,0,0.17);
|
248 |
+
text-align:center;
|
249 |
+
position:relative;
|
250 |
+
}
|
251 |
+
|
252 |
+
.analyst-btn-secondary:disabled {
|
253 |
+
background-color: #6C8CD5;
|
254 |
+
cursor: not-allowed;
|
255 |
+
}
|
256 |
+
|
257 |
+
.analyst-btn-secondary:active{
|
258 |
+
top:0.1em;
|
259 |
+
}
|
260 |
+
@media all and (max-width:30em){
|
261 |
+
.analyst-btn-secondary {
|
262 |
+
display:block;
|
263 |
+
margin:0.4em auto;
|
264 |
+
}
|
265 |
+
}
|
266 |
+
|
267 |
+
.analyst-notice {
|
268 |
+
padding-right: 38px;
|
269 |
+
position: relative;
|
270 |
+
margin-bottom: 30px !important;
|
271 |
+
}
|
272 |
+
|
273 |
+
.analyst-notice .analyst-plugin-name {
|
274 |
+
background-color: #00000024;
|
275 |
+
padding-left: 7px;
|
276 |
+
padding-right: 7px;
|
277 |
+
position: absolute;
|
278 |
+
top: 100%;
|
279 |
+
border-radius: 0 0 5px 5px;
|
280 |
+
}
|
analyst/assets/img/pencil.png
ADDED
Binary file
|
analyst/assets/img/shield_question.png
ADDED
Binary file
|
analyst/assets/img/shield_success.png
ADDED
Binary file
|
analyst/assets/img/smile.png
ADDED
Binary file
|
analyst/assets/index.php
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
1 |
+
<?php
|
2 |
+
// Silence is golden.
|
analyst/assets/js/customize.js
ADDED
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
(function ($) {
|
2 |
+
$(document).on('click', '.analyst-notice-dismiss', function () {
|
3 |
+
var id = $(this).attr('analyst-notice-id');
|
4 |
+
var self = this;
|
5 |
+
|
6 |
+
$.post(ajaxurl, {action: 'analyst_notification_dismiss', id: id})
|
7 |
+
.done(function () {
|
8 |
+
$(self).parent().fadeOut()
|
9 |
+
})
|
10 |
+
})
|
11 |
+
|
12 |
+
var url = new URL(window.location.href)
|
13 |
+
|
14 |
+
if (url.searchParams.has('verify')) {
|
15 |
+
var pluginId = url.searchParams.get('plugin_id')
|
16 |
+
|
17 |
+
$.ajax({
|
18 |
+
url: ajaxurl,
|
19 |
+
method: 'POST',
|
20 |
+
data: {
|
21 |
+
action: 'analyst_install_verified_' + pluginId,
|
22 |
+
},
|
23 |
+
success: function () {
|
24 |
+
// Refresh page without query params
|
25 |
+
window.location.href = window.location.origin + window.location.pathname
|
26 |
+
}
|
27 |
+
})
|
28 |
+
}
|
29 |
+
})(jQuery)
|
analyst/autoload.php
ADDED
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
require_once __DIR__ . '/src/helpers.php';
|
4 |
+
|
5 |
+
require_once __DIR__ . '/src/Contracts/HttpClientContract.php';
|
6 |
+
require_once __DIR__ . '/src/Contracts/RequestContract.php';
|
7 |
+
require_once __DIR__ . '/src/Contracts/RequestorContract.php';
|
8 |
+
require_once __DIR__ . '/src/Contracts/TrackerContract.php';
|
9 |
+
require_once __DIR__ . '/src/Contracts/CacheContract.php';
|
10 |
+
|
11 |
+
require_once __DIR__ . '/src/Core/AbstractFactory.php';
|
12 |
+
|
13 |
+
require_once __DIR__ . '/src/Cache/DatabaseCache.php';
|
14 |
+
|
15 |
+
require_once __DIR__ . '/src/Account/Account.php';
|
16 |
+
require_once __DIR__ . '/src/Account/AccountData.php';
|
17 |
+
require_once __DIR__ . '/src/Account/AccountDataFactory.php';
|
18 |
+
require_once __DIR__ . '/src/Contracts/AnalystContract.php';
|
19 |
+
|
20 |
+
require_once __DIR__ . '/src/Http/Requests/AbstractLoggerRequest.php';
|
21 |
+
require_once __DIR__ . '/src/Http/Requests/ActivateRequest.php';
|
22 |
+
require_once __DIR__ . '/src/Http/Requests/DeactivateRequest.php';
|
23 |
+
require_once __DIR__ . '/src/Http/Requests/InstallRequest.php';
|
24 |
+
require_once __DIR__ . '/src/Http/Requests/OptInRequest.php';
|
25 |
+
require_once __DIR__ . '/src/Http/Requests/OptOutRequest.php';
|
26 |
+
require_once __DIR__ . '/src/Http/Requests/UninstallRequest.php';
|
27 |
+
|
28 |
+
require_once __DIR__ . '/src/Http/CurlHttpClient.php';
|
29 |
+
require_once __DIR__ . '/src/Http/DummyHttpClient.php';
|
30 |
+
require_once __DIR__ . '/src/Http/WordPressHttpClient.php';
|
31 |
+
|
32 |
+
require_once __DIR__ . '/src/Notices/Notice.php';
|
33 |
+
require_once __DIR__ . '/src/Notices/NoticeFactory.php';
|
34 |
+
|
35 |
+
require_once __DIR__ . '/src/Analyst.php';
|
36 |
+
require_once __DIR__ . '/src/ApiRequestor.php';
|
37 |
+
require_once __DIR__ . '/src/ApiResponse.php';
|
38 |
+
require_once __DIR__ . '/src/Collector.php';
|
39 |
+
require_once __DIR__ . '/src/Mutator.php';
|
40 |
+
|
analyst/index.php
ADDED
@@ -0,0 +1,2 @@
|
|
|
|
|
1 |
+
<?php
|
2 |
+
// Silence
|
analyst/main.php
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
require_once 'sdk_resolver.php';
|
4 |
+
|
5 |
+
|
6 |
+
if (!function_exists('analyst_init')) {
|
7 |
+
/**
|
8 |
+
* Initialize analyst
|
9 |
+
*
|
10 |
+
* @param array $options
|
11 |
+
*/
|
12 |
+
function analyst_init ($options) {
|
13 |
+
// Try resolve latest supported SDK
|
14 |
+
// In case resolving is failed exit the execution
|
15 |
+
try {
|
16 |
+
analyst_resolve_sdk($options['base-dir']);
|
17 |
+
} catch (Exception $exception) {
|
18 |
+
error_log('[ANALYST] Cannot resolve any supported SDK');
|
19 |
+
return;
|
20 |
+
}
|
21 |
+
|
22 |
+
try {
|
23 |
+
global /** @var Analyst\Analyst $analyst */
|
24 |
+
$analyst;
|
25 |
+
|
26 |
+
// Set global instance of analyst
|
27 |
+
if (!$analyst) {
|
28 |
+
$analyst = Analyst\Analyst::getInstance();
|
29 |
+
}
|
30 |
+
|
31 |
+
$analyst->registerAccount(new Account\Account($options['client-id'], $options['client-secret'], $options['base-dir']));
|
32 |
+
} catch (Exception $e) {
|
33 |
+
error_log('Analyst SDK receive an error: [' . $e->getMessage() . '] Please contact our support at support@analyst.com');
|
34 |
+
}
|
35 |
+
}
|
36 |
+
}
|
analyst/sdk_resolver.php
ADDED
@@ -0,0 +1,79 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
if (!function_exists('analyst_resolve_sdk')) {
|
4 |
+
|
5 |
+
/**
|
6 |
+
* Resolve supported sdk versions and load latest supported one
|
7 |
+
* also bootstrap sdk with autoloader
|
8 |
+
*
|
9 |
+
* @since 1.1.3
|
10 |
+
*
|
11 |
+
* @param null $thisPluginPath
|
12 |
+
* @return void
|
13 |
+
* @throws Exception
|
14 |
+
*/
|
15 |
+
function analyst_resolve_sdk($thisPluginPath = null) {
|
16 |
+
static $loaded = false;
|
17 |
+
|
18 |
+
// Exit if we already resolved SDK
|
19 |
+
if ($loaded) return;
|
20 |
+
|
21 |
+
$plugins = get_option('active_plugins');
|
22 |
+
|
23 |
+
if ($thisPluginPath) {
|
24 |
+
array_push($plugins, plugin_basename($thisPluginPath));
|
25 |
+
}
|
26 |
+
|
27 |
+
$pluginsFolder = WP_PLUGIN_DIR;
|
28 |
+
|
29 |
+
$possibleSDKs = array_map(function ($path) use ($pluginsFolder) {
|
30 |
+
$sdkFolder = sprintf('%s/%s/analyst/', $pluginsFolder, dirname($path));
|
31 |
+
|
32 |
+
$sdkFolder = str_replace('\\', '/', $sdkFolder);
|
33 |
+
|
34 |
+
$versionPath = $sdkFolder . 'version.php';
|
35 |
+
|
36 |
+
if (file_exists($versionPath)) {
|
37 |
+
return require $versionPath;
|
38 |
+
}
|
39 |
+
|
40 |
+
return false;
|
41 |
+
}, $plugins);
|
42 |
+
|
43 |
+
global $wp_version;
|
44 |
+
|
45 |
+
// Filter out plugins which has no SDK
|
46 |
+
$SDKs = array_filter($possibleSDKs, function ($s) {return is_array($s);});
|
47 |
+
|
48 |
+
// Filter SDKs which is supported by PHP and WP
|
49 |
+
$supported = array_values(array_filter($SDKs, function ($sdk) use($wp_version) {
|
50 |
+
$phpSupported = version_compare(PHP_VERSION, $sdk['php']) >= 0;
|
51 |
+
$wpSupported = version_compare($wp_version, $sdk['wp']) >= 0;
|
52 |
+
|
53 |
+
return $phpSupported && $wpSupported;
|
54 |
+
}));
|
55 |
+
|
56 |
+
// Sort SDK by version in descending order
|
57 |
+
uasort($supported, function ($x, $y) {
|
58 |
+
return version_compare($y['sdk'], $x['sdk']);
|
59 |
+
});
|
60 |
+
|
61 |
+
// Reset sorted values keys
|
62 |
+
$supported = array_values($supported);
|
63 |
+
|
64 |
+
if (!isset($supported[0])) {
|
65 |
+
throw new Exception('There is no SDK which is support current PHP version and WP version');
|
66 |
+
}
|
67 |
+
|
68 |
+
// Autoload files for supported SDK
|
69 |
+
$autoloaderPath = str_replace(
|
70 |
+
'\\',
|
71 |
+
'/',
|
72 |
+
sprintf('%s/autoload.php', $supported[0]['path'])
|
73 |
+
);
|
74 |
+
|
75 |
+
require_once $autoloaderPath;
|
76 |
+
|
77 |
+
$loaded = true;
|
78 |
+
}
|
79 |
+
}
|
analyst/src/Account/Account.php
ADDED
@@ -0,0 +1,584 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Account;
|
4 |
+
|
5 |
+
use Analyst\Analyst;
|
6 |
+
use Analyst\ApiRequestor;
|
7 |
+
use Analyst\Cache\DatabaseCache;
|
8 |
+
use Analyst\Collector;
|
9 |
+
use Analyst\Http\Requests\ActivateRequest;
|
10 |
+
use Analyst\Http\Requests\DeactivateRequest;
|
11 |
+
use Analyst\Http\Requests\InstallRequest;
|
12 |
+
use Analyst\Http\Requests\OptInRequest;
|
13 |
+
use Analyst\Http\Requests\OptOutRequest;
|
14 |
+
use Analyst\Http\Requests\UninstallRequest;
|
15 |
+
use Analyst\Notices\Notice;
|
16 |
+
use Analyst\Notices\NoticeFactory;
|
17 |
+
use Analyst\Contracts\TrackerContract;
|
18 |
+
use Analyst\Contracts\RequestorContract;
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Class Account
|
22 |
+
*
|
23 |
+
* This is plugin's account object
|
24 |
+
*/
|
25 |
+
class Account implements TrackerContract
|
26 |
+
{
|
27 |
+
/**
|
28 |
+
* Account id
|
29 |
+
*
|
30 |
+
* @var string
|
31 |
+
*/
|
32 |
+
protected $id;
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Basename of plugin
|
36 |
+
*
|
37 |
+
* @var string
|
38 |
+
*/
|
39 |
+
protected $path;
|
40 |
+
|
41 |
+
/**
|
42 |
+
* Whether plugin is active or not
|
43 |
+
*
|
44 |
+
* @var bool
|
45 |
+
*/
|
46 |
+
protected $isInstalled = false;
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Is user sign in for data tracking
|
50 |
+
*
|
51 |
+
* @var bool
|
52 |
+
*/
|
53 |
+
protected $isOptedIn = false;
|
54 |
+
|
55 |
+
/**
|
56 |
+
* Is user accepted permissions grant
|
57 |
+
* for collection site data
|
58 |
+
*
|
59 |
+
* @var bool
|
60 |
+
*/
|
61 |
+
protected $isSigned = false;
|
62 |
+
|
63 |
+
/**
|
64 |
+
* Is user ever resolved install modal window?
|
65 |
+
*
|
66 |
+
* @var bool
|
67 |
+
*/
|
68 |
+
protected $isInstallResolved = false;
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Public secret code
|
72 |
+
*
|
73 |
+
* @var string
|
74 |
+
*/
|
75 |
+
protected $clientSecret;
|
76 |
+
|
77 |
+
/**
|
78 |
+
* @var AccountData
|
79 |
+
*/
|
80 |
+
protected $data;
|
81 |
+
|
82 |
+
/**
|
83 |
+
* Base plugin path
|
84 |
+
*
|
85 |
+
* @var string
|
86 |
+
*/
|
87 |
+
protected $basePluginPath;
|
88 |
+
|
89 |
+
/**
|
90 |
+
* @var RequestorContract
|
91 |
+
*/
|
92 |
+
protected $requestor;
|
93 |
+
|
94 |
+
/**
|
95 |
+
* @var Collector
|
96 |
+
*/
|
97 |
+
protected $collector;
|
98 |
+
|
99 |
+
/**
|
100 |
+
* Account constructor.
|
101 |
+
* @param $id
|
102 |
+
* @param $secret
|
103 |
+
* @param $baseDir
|
104 |
+
*/
|
105 |
+
public function __construct($id, $secret, $baseDir)
|
106 |
+
{
|
107 |
+
$this->id = $id;
|
108 |
+
$this->clientSecret = $secret;
|
109 |
+
|
110 |
+
$this->path = $baseDir;
|
111 |
+
|
112 |
+
$this->basePluginPath = plugin_basename($baseDir);
|
113 |
+
}
|
114 |
+
|
115 |
+
/**
|
116 |
+
* @return string
|
117 |
+
*/
|
118 |
+
public function getPath()
|
119 |
+
{
|
120 |
+
return $this->path;
|
121 |
+
}
|
122 |
+
|
123 |
+
/**
|
124 |
+
* @param string $path
|
125 |
+
*/
|
126 |
+
public function setPath($path)
|
127 |
+
{
|
128 |
+
$this->data->setPath($path);
|
129 |
+
|
130 |
+
$this->path = $path;
|
131 |
+
}
|
132 |
+
|
133 |
+
/**
|
134 |
+
* @return bool
|
135 |
+
*/
|
136 |
+
public function isOptedIn()
|
137 |
+
{
|
138 |
+
return $this->isOptedIn;
|
139 |
+
}
|
140 |
+
|
141 |
+
/**
|
142 |
+
* @param bool $isOptedIn
|
143 |
+
*/
|
144 |
+
public function setIsOptedIn($isOptedIn)
|
145 |
+
{
|
146 |
+
$this->data->setIsOptedIn($isOptedIn);
|
147 |
+
|
148 |
+
$this->isOptedIn = $isOptedIn;
|
149 |
+
}
|
150 |
+
|
151 |
+
/**
|
152 |
+
* Whether plugin is active
|
153 |
+
*
|
154 |
+
* @return bool
|
155 |
+
*/
|
156 |
+
public function isActive()
|
157 |
+
{
|
158 |
+
return is_plugin_active($this->path);
|
159 |
+
}
|
160 |
+
|
161 |
+
/**
|
162 |
+
* @param string $id
|
163 |
+
*/
|
164 |
+
public function setId($id)
|
165 |
+
{
|
166 |
+
$this->id = $id;
|
167 |
+
}
|
168 |
+
|
169 |
+
/**
|
170 |
+
* @return string
|
171 |
+
*/
|
172 |
+
public function getId()
|
173 |
+
{
|
174 |
+
return $this->id;
|
175 |
+
}
|
176 |
+
|
177 |
+
/**
|
178 |
+
* @return bool
|
179 |
+
*/
|
180 |
+
public function isInstalled()
|
181 |
+
{
|
182 |
+
return $this->isInstalled;
|
183 |
+
}
|
184 |
+
|
185 |
+
/**
|
186 |
+
* @param bool $isInstalled
|
187 |
+
*/
|
188 |
+
public function setIsInstalled($isInstalled)
|
189 |
+
{
|
190 |
+
$this->data->setIsInstalled($isInstalled);
|
191 |
+
|
192 |
+
$this->isInstalled = $isInstalled;
|
193 |
+
}
|
194 |
+
|
195 |
+
/**
|
196 |
+
* Should register activation and deactivation
|
197 |
+
* event hooks
|
198 |
+
*
|
199 |
+
* @return void
|
200 |
+
*/
|
201 |
+
public function registerHooks()
|
202 |
+
{
|
203 |
+
register_activation_hook($this->basePluginPath, [&$this, 'onActivePluginListener']);
|
204 |
+
register_uninstall_hook($this->basePluginPath, ['Account\Account', 'onUninstallPluginListener']);
|
205 |
+
|
206 |
+
$this->addFilter('plugin_action_links', [&$this, 'onRenderActionLinksHook']);
|
207 |
+
|
208 |
+
$this->addAjax('analyst_opt_in', [&$this, 'onOptInListener']);
|
209 |
+
$this->addAjax('analyst_opt_out', [&$this, 'onOptOutListener']);
|
210 |
+
$this->addAjax('analyst_plugin_deactivate', [&$this, 'onDeactivatePluginListener']);
|
211 |
+
$this->addAjax('analyst_install', [&$this, 'onInstallListener']);
|
212 |
+
$this->addAjax('analyst_skip_install', [&$this, 'onSkipInstallListener']);
|
213 |
+
$this->addAjax('analyst_install_verified', [&$this, 'onInstallVerifiedListener']);
|
214 |
+
}
|
215 |
+
|
216 |
+
/**
|
217 |
+
* Will fire when admin activates plugin
|
218 |
+
*
|
219 |
+
* @return void
|
220 |
+
*/
|
221 |
+
public function onActivePluginListener()
|
222 |
+
{
|
223 |
+
if (!$this->isInstallResolved()) {
|
224 |
+
DatabaseCache::getInstance()->put('plugin_to_install', $this->id);
|
225 |
+
}
|
226 |
+
|
227 |
+
if (!$this->isAllowingLogging()) return;
|
228 |
+
|
229 |
+
ActivateRequest::make($this->collector, $this->id, $this->path)
|
230 |
+
->execute($this->requestor);
|
231 |
+
|
232 |
+
$this->setIsInstalled(true);
|
233 |
+
|
234 |
+
AccountDataFactory::syncData();
|
235 |
+
}
|
236 |
+
|
237 |
+
/**
|
238 |
+
* Will fire when admin deactivates plugin
|
239 |
+
*
|
240 |
+
* @return void
|
241 |
+
*/
|
242 |
+
public function onDeactivatePluginListener()
|
243 |
+
{
|
244 |
+
if (!$this->isAllowingLogging()) return;
|
245 |
+
|
246 |
+
$question = isset($_POST['question']) ? stripslashes($_POST['question']) : null;
|
247 |
+
$reason = isset($_POST['reason']) ? stripslashes($_POST['reason']) : null;
|
248 |
+
|
249 |
+
DeactivateRequest::make($this->collector, $this->id, $this->path, $question, $reason)
|
250 |
+
->execute($this->requestor);
|
251 |
+
|
252 |
+
$this->setIsInstalled(false);
|
253 |
+
|
254 |
+
AccountDataFactory::syncData();
|
255 |
+
|
256 |
+
wp_send_json_success();
|
257 |
+
}
|
258 |
+
|
259 |
+
/**
|
260 |
+
* Will fire when user opted in
|
261 |
+
*
|
262 |
+
* @return void
|
263 |
+
*/
|
264 |
+
public function onOptInListener()
|
265 |
+
{
|
266 |
+
OptInRequest::make($this->collector, $this->id, $this->path)->execute($this->requestor);
|
267 |
+
|
268 |
+
$this->setIsOptedIn(true);
|
269 |
+
|
270 |
+
AccountDataFactory::syncData();
|
271 |
+
|
272 |
+
wp_die();
|
273 |
+
}
|
274 |
+
|
275 |
+
/**
|
276 |
+
* Will fire when user opted out
|
277 |
+
*
|
278 |
+
* @return void
|
279 |
+
*/
|
280 |
+
public function onOptOutListener()
|
281 |
+
{
|
282 |
+
OptOutRequest::make($this->collector, $this->id, $this->path)->execute($this->requestor);
|
283 |
+
|
284 |
+
$this->setIsOptedIn(false);
|
285 |
+
|
286 |
+
AccountDataFactory::syncData();
|
287 |
+
|
288 |
+
wp_send_json_success();
|
289 |
+
}
|
290 |
+
|
291 |
+
/**
|
292 |
+
* Will fire when user accept opt-in
|
293 |
+
* at first time
|
294 |
+
*
|
295 |
+
* @return void
|
296 |
+
*/
|
297 |
+
public function onInstallListener()
|
298 |
+
{
|
299 |
+
$cache = DatabaseCache::getInstance();
|
300 |
+
|
301 |
+
// Set flag to true which indicates that install is resolved
|
302 |
+
// also remove install plugin id from cache
|
303 |
+
$this->setIsInstallResolved(true);
|
304 |
+
$cache->delete('plugin_to_install');
|
305 |
+
|
306 |
+
InstallRequest::make($this->collector, $this->id, $this->path)->execute($this->requestor);
|
307 |
+
|
308 |
+
$this->setIsSigned(true);
|
309 |
+
|
310 |
+
$this->setIsOptedIn(true);
|
311 |
+
|
312 |
+
$factory = NoticeFactory::instance();
|
313 |
+
|
314 |
+
$message = sprintf('Please confirm your email by clicking on the link we sent to %s. This makes sure you’re not a bot.', $this->collector->getGeneralEmailAddress());
|
315 |
+
|
316 |
+
$notificationId = uniqid();
|
317 |
+
|
318 |
+
$notice = Notice::make(
|
319 |
+
$notificationId,
|
320 |
+
$this->getId(),
|
321 |
+
$message,
|
322 |
+
$this->collector->getPluginName($this->path)
|
323 |
+
);
|
324 |
+
|
325 |
+
$factory->addNotice($notice);
|
326 |
+
|
327 |
+
AccountDataFactory::syncData();
|
328 |
+
|
329 |
+
// Set email confirmation notification id to cache
|
330 |
+
// se we can extract and remove it when user confirmed email
|
331 |
+
$cache->put(
|
332 |
+
sprintf('account_email_confirmation_%s', $this->getId()),
|
333 |
+
$notificationId
|
334 |
+
);
|
335 |
+
|
336 |
+
wp_send_json_success();
|
337 |
+
}
|
338 |
+
|
339 |
+
/**
|
340 |
+
* Will fire when user skipped installation
|
341 |
+
*
|
342 |
+
* @return void
|
343 |
+
*/
|
344 |
+
public function onSkipInstallListener()
|
345 |
+
{
|
346 |
+
// Set flag to true which indicates that install is resolved
|
347 |
+
// also remove install plugin id from cache
|
348 |
+
$this->setIsInstallResolved(true);
|
349 |
+
DatabaseCache::getInstance()->delete('plugin_to_install');
|
350 |
+
}
|
351 |
+
|
352 |
+
/**
|
353 |
+
* Will fire when user delete plugin through admin panel.
|
354 |
+
* This action will happen if admin at least once
|
355 |
+
* activated the plugin.
|
356 |
+
*
|
357 |
+
* @return void
|
358 |
+
* @throws \Exception
|
359 |
+
*/
|
360 |
+
public static function onUninstallPluginListener()
|
361 |
+
{
|
362 |
+
$factory = AccountDataFactory::instance();
|
363 |
+
|
364 |
+
$pluginFile = substr(current_filter(), strlen( 'uninstall_' ));
|
365 |
+
|
366 |
+
$account = $factory->getAccountDataByBasePath($pluginFile);
|
367 |
+
|
368 |
+
// If account somehow is not found, exit the execution
|
369 |
+
if (!$account) return;
|
370 |
+
|
371 |
+
$analyst = Analyst::getInstance();
|
372 |
+
|
373 |
+
$collector = new Collector($analyst);
|
374 |
+
|
375 |
+
$requestor = new ApiRequestor($account->getId(), $account->getSecret(), $analyst->getApiBase());
|
376 |
+
|
377 |
+
// Just send request to log uninstall event not caring about response
|
378 |
+
UninstallRequest::make($collector, $account->getId(), $account->getPath())->execute($requestor);
|
379 |
+
|
380 |
+
$factory->sync();
|
381 |
+
}
|
382 |
+
|
383 |
+
/**
|
384 |
+
* Fires when used verified his account
|
385 |
+
*/
|
386 |
+
public function onInstallVerifiedListener()
|
387 |
+
{
|
388 |
+
$factory = NoticeFactory::instance();
|
389 |
+
|
390 |
+
$notice = Notice::make(
|
391 |
+
uniqid(),
|
392 |
+
$this->getId(),
|
393 |
+
'Thank you for confirming your email.',
|
394 |
+
$this->collector->getPluginName($this->path)
|
395 |
+
);
|
396 |
+
|
397 |
+
$factory->addNotice($notice);
|
398 |
+
|
399 |
+
// Remove confirmation notification
|
400 |
+
$confirmationNotificationId = DatabaseCache::getInstance()->pop(sprintf('account_email_confirmation_%s', $this->getId()));
|
401 |
+
$factory->remove($confirmationNotificationId);
|
402 |
+
|
403 |
+
AccountDataFactory::syncData();
|
404 |
+
|
405 |
+
wp_send_json_success();
|
406 |
+
}
|
407 |
+
|
408 |
+
/**
|
409 |
+
* Will fire when wp renders plugin
|
410 |
+
* action buttons
|
411 |
+
*
|
412 |
+
* @param $defaultLinks
|
413 |
+
* @return array
|
414 |
+
*/
|
415 |
+
public function onRenderActionLinksHook($defaultLinks)
|
416 |
+
{
|
417 |
+
$customLinks = [];
|
418 |
+
|
419 |
+
$customLinks[] = $this->isOptedIn()
|
420 |
+
? '<a class="analyst-action-opt analyst-opt-out" analyst-plugin-id="' . $this->getId() . '" analyst-plugin-signed="' . (int) $this->isSigned() . '">Opt Out</a>'
|
421 |
+
: '<a class="analyst-action-opt analyst-opt-in" analyst-plugin-id="' . $this->getId() . '" analyst-plugin-signed="' . (int) $this->isSigned() . '">Opt In</a>';
|
422 |
+
|
423 |
+
// Append anchor to find specific deactivation link
|
424 |
+
if (isset($defaultLinks['deactivate'])) {
|
425 |
+
$defaultLinks['deactivate'] .= '<span analyst-plugin-id="' . $this->getId() . '" analyst-plugin-opted-in="' . (int) $this->isOptedIn() . '"></span>';
|
426 |
+
}
|
427 |
+
|
428 |
+
return array_merge($customLinks, $defaultLinks);
|
429 |
+
}
|
430 |
+
|
431 |
+
/**
|
432 |
+
* @return AccountData
|
433 |
+
*/
|
434 |
+
public function getData()
|
435 |
+
{
|
436 |
+
return $this->data;
|
437 |
+
}
|
438 |
+
|
439 |
+
/**
|
440 |
+
* @param AccountData $data
|
441 |
+
*/
|
442 |
+
public function setData(AccountData $data)
|
443 |
+
{
|
444 |
+
$this->data = $data;
|
445 |
+
|
446 |
+
$this->setIsOptedIn($data->isOptedIn());
|
447 |
+
$this->setIsInstalled($data->isInstalled());
|
448 |
+
$this->setIsSigned($data->isSigned());
|
449 |
+
$this->setIsInstallResolved($data->isInstallResolved());
|
450 |
+
}
|
451 |
+
|
452 |
+
/**
|
453 |
+
* Resolves valid action name
|
454 |
+
* based on client id
|
455 |
+
*
|
456 |
+
* @param $action
|
457 |
+
* @return string
|
458 |
+
*/
|
459 |
+
private function resolveActionName($action)
|
460 |
+
{
|
461 |
+
return sprintf('%s_%s', $action, $this->id);
|
462 |
+
}
|
463 |
+
|
464 |
+
/**
|
465 |
+
* Register action for current plugin
|
466 |
+
*
|
467 |
+
* @param $action
|
468 |
+
* @param $callback
|
469 |
+
*/
|
470 |
+
private function addFilter($action, $callback)
|
471 |
+
{
|
472 |
+
$validAction = sprintf('%s_%s', $action, $this->basePluginPath);
|
473 |
+
|
474 |
+
add_filter($validAction, $callback, 10);
|
475 |
+
}
|
476 |
+
|
477 |
+
/**
|
478 |
+
* Add ajax action for current plugin
|
479 |
+
*
|
480 |
+
* @param $action
|
481 |
+
* @param $callback
|
482 |
+
* @param bool $raw Format action ??
|
483 |
+
*/
|
484 |
+
private function addAjax($action, $callback, $raw = false)
|
485 |
+
{
|
486 |
+
$validAction = $raw ? $action : sprintf('%s%s', 'wp_ajax_', $this->resolveActionName($action));
|
487 |
+
|
488 |
+
add_action($validAction, $callback);
|
489 |
+
}
|
490 |
+
|
491 |
+
/**
|
492 |
+
* @return bool
|
493 |
+
*/
|
494 |
+
public function isSigned()
|
495 |
+
{
|
496 |
+
return $this->isSigned;
|
497 |
+
}
|
498 |
+
|
499 |
+
/**
|
500 |
+
* @param bool $isSigned
|
501 |
+
*/
|
502 |
+
public function setIsSigned($isSigned)
|
503 |
+
{
|
504 |
+
$this->data->setIsSigned($isSigned);
|
505 |
+
|
506 |
+
$this->isSigned = $isSigned;
|
507 |
+
}
|
508 |
+
|
509 |
+
/**
|
510 |
+
* @return RequestorContract
|
511 |
+
*/
|
512 |
+
public function getRequestor()
|
513 |
+
{
|
514 |
+
return $this->requestor;
|
515 |
+
}
|
516 |
+
|
517 |
+
/**
|
518 |
+
* @param RequestorContract $requestor
|
519 |
+
*/
|
520 |
+
public function setRequestor(RequestorContract $requestor)
|
521 |
+
{
|
522 |
+
$this->requestor = $requestor;
|
523 |
+
}
|
524 |
+
|
525 |
+
/**
|
526 |
+
* @return string
|
527 |
+
*/
|
528 |
+
public function getClientSecret()
|
529 |
+
{
|
530 |
+
return $this->clientSecret;
|
531 |
+
}
|
532 |
+
|
533 |
+
/**
|
534 |
+
* @return Collector
|
535 |
+
*/
|
536 |
+
public function getCollector()
|
537 |
+
{
|
538 |
+
return $this->collector;
|
539 |
+
}
|
540 |
+
|
541 |
+
/**
|
542 |
+
* @param Collector $collector
|
543 |
+
*/
|
544 |
+
public function setCollector(Collector $collector)
|
545 |
+
{
|
546 |
+
$this->collector = $collector;
|
547 |
+
}
|
548 |
+
|
549 |
+
/**
|
550 |
+
* Do we allowing logging
|
551 |
+
*
|
552 |
+
* @return bool
|
553 |
+
*/
|
554 |
+
public function isAllowingLogging()
|
555 |
+
{
|
556 |
+
return $this->isOptedIn;
|
557 |
+
}
|
558 |
+
|
559 |
+
/**
|
560 |
+
* @return string
|
561 |
+
*/
|
562 |
+
public function getBasePluginPath()
|
563 |
+
{
|
564 |
+
return $this->basePluginPath;
|
565 |
+
}
|
566 |
+
|
567 |
+
/**
|
568 |
+
* @return bool
|
569 |
+
*/
|
570 |
+
public function isInstallResolved()
|
571 |
+
{
|
572 |
+
return $this->isInstallResolved;
|
573 |
+
}
|
574 |
+
|
575 |
+
/**
|
576 |
+
* @param bool $isInstallResolved
|
577 |
+
*/
|
578 |
+
public function setIsInstallResolved($isInstallResolved)
|
579 |
+
{
|
580 |
+
$this->data->setIsInstallResolved($isInstallResolved);
|
581 |
+
|
582 |
+
$this->isInstallResolved = $isInstallResolved;
|
583 |
+
}
|
584 |
+
}
|
analyst/src/Account/AccountData.php
ADDED
@@ -0,0 +1,176 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Account;
|
4 |
+
|
5 |
+
/**
|
6 |
+
* Class AccountData is the data holder
|
7 |
+
* for Analyst\Account\Account class
|
8 |
+
* which is unserialized from database
|
9 |
+
*/
|
10 |
+
class AccountData
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Account id
|
14 |
+
*
|
15 |
+
* @var string
|
16 |
+
*/
|
17 |
+
protected $id;
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Account secret key
|
21 |
+
*
|
22 |
+
* @var string
|
23 |
+
*/
|
24 |
+
protected $secret;
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Basename of plugin
|
28 |
+
*
|
29 |
+
* @var string
|
30 |
+
*/
|
31 |
+
protected $path;
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Whether admin accepted opt in
|
35 |
+
* terms and permissions
|
36 |
+
*
|
37 |
+
* @var bool
|
38 |
+
*/
|
39 |
+
protected $isInstalled = false;
|
40 |
+
|
41 |
+
/**
|
42 |
+
* Is user sign in for data tracking
|
43 |
+
*
|
44 |
+
* @var bool
|
45 |
+
*/
|
46 |
+
protected $isOptedIn = false;
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Is user accepted permissions grant
|
50 |
+
* for collection site data
|
51 |
+
*
|
52 |
+
* @var bool
|
53 |
+
*/
|
54 |
+
protected $isSigned = false;
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Is user ever resolved install modal window?
|
58 |
+
*
|
59 |
+
* @var bool
|
60 |
+
*/
|
61 |
+
protected $isInstallResolved;
|
62 |
+
|
63 |
+
/**
|
64 |
+
* @return string
|
65 |
+
*/
|
66 |
+
public function getId()
|
67 |
+
{
|
68 |
+
return $this->id;
|
69 |
+
}
|
70 |
+
|
71 |
+
/**
|
72 |
+
* @param string $id
|
73 |
+
*/
|
74 |
+
public function setId($id)
|
75 |
+
{
|
76 |
+
$this->id = $id;
|
77 |
+
}
|
78 |
+
|
79 |
+
/**
|
80 |
+
* @param string $path
|
81 |
+
* @return AccountData
|
82 |
+
*/
|
83 |
+
public function setPath($path)
|
84 |
+
{
|
85 |
+
$this->path = $path;
|
86 |
+
return $this;
|
87 |
+
}
|
88 |
+
|
89 |
+
/**
|
90 |
+
* @return bool
|
91 |
+
*/
|
92 |
+
public function isInstalled()
|
93 |
+
{
|
94 |
+
return $this->isInstalled;
|
95 |
+
}
|
96 |
+
|
97 |
+
/**
|
98 |
+
* @param bool $isInstalled
|
99 |
+
*/
|
100 |
+
public function setIsInstalled($isInstalled)
|
101 |
+
{
|
102 |
+
$this->isInstalled = $isInstalled;
|
103 |
+
}
|
104 |
+
|
105 |
+
/**
|
106 |
+
* @return bool
|
107 |
+
*/
|
108 |
+
public function isOptedIn()
|
109 |
+
{
|
110 |
+
return $this->isOptedIn;
|
111 |
+
}
|
112 |
+
|
113 |
+
/**
|
114 |
+
* @param bool $isOptedIn
|
115 |
+
*/
|
116 |
+
public function setIsOptedIn($isOptedIn)
|
117 |
+
{
|
118 |
+
$this->isOptedIn = $isOptedIn;
|
119 |
+
}
|
120 |
+
|
121 |
+
/**
|
122 |
+
* @return bool
|
123 |
+
*/
|
124 |
+
public function isSigned()
|
125 |
+
{
|
126 |
+
return $this->isSigned;
|
127 |
+
}
|
128 |
+
|
129 |
+
/**
|
130 |
+
* @param bool $isSigned
|
131 |
+
*/
|
132 |
+
public function setIsSigned($isSigned)
|
133 |
+
{
|
134 |
+
$this->isSigned = $isSigned;
|
135 |
+
}
|
136 |
+
|
137 |
+
/**
|
138 |
+
* @return string
|
139 |
+
*/
|
140 |
+
public function getPath()
|
141 |
+
{
|
142 |
+
return $this->path;
|
143 |
+
}
|
144 |
+
|
145 |
+
/**
|
146 |
+
* @return string
|
147 |
+
*/
|
148 |
+
public function getSecret()
|
149 |
+
{
|
150 |
+
return $this->secret;
|
151 |
+
}
|
152 |
+
|
153 |
+
/**
|
154 |
+
* @param string $secret
|
155 |
+
*/
|
156 |
+
public function setSecret($secret)
|
157 |
+
{
|
158 |
+
$this->secret = $secret;
|
159 |
+
}
|
160 |
+
|
161 |
+
/**
|
162 |
+
* @return bool
|
163 |
+
*/
|
164 |
+
public function isInstallResolved()
|
165 |
+
{
|
166 |
+
return $this->isInstallResolved;
|
167 |
+
}
|
168 |
+
|
169 |
+
/**
|
170 |
+
* @param bool $isInstallResolved
|
171 |
+
*/
|
172 |
+
public function setIsInstallResolved($isInstallResolved)
|
173 |
+
{
|
174 |
+
$this->isInstallResolved = $isInstallResolved;
|
175 |
+
}
|
176 |
+
}
|
analyst/src/Account/AccountDataFactory.php
ADDED
@@ -0,0 +1,125 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Account;
|
4 |
+
|
5 |
+
|
6 |
+
use Analyst\Core\AbstractFactory;
|
7 |
+
|
8 |
+
/**
|
9 |
+
* Class AccountDataFactory
|
10 |
+
*
|
11 |
+
* Holds information about this
|
12 |
+
* wordpress project plugins accounts
|
13 |
+
*
|
14 |
+
*/
|
15 |
+
class AccountDataFactory extends AbstractFactory
|
16 |
+
{
|
17 |
+
private static $instance;
|
18 |
+
|
19 |
+
CONST OPTIONS_KEY = 'analyst_accounts_data';
|
20 |
+
|
21 |
+
/**
|
22 |
+
* @var AccountData[]
|
23 |
+
*/
|
24 |
+
protected $accounts = [];
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Read factory from options or make fresh instance
|
28 |
+
*
|
29 |
+
* @return static
|
30 |
+
*/
|
31 |
+
public static function instance()
|
32 |
+
{
|
33 |
+
if (!static::$instance) {
|
34 |
+
$raw = get_option(self::OPTIONS_KEY);
|
35 |
+
|
36 |
+
// In case object is already unserialized
|
37 |
+
// and instance of AccountDataFactory we
|
38 |
+
// return it, in other case deal with
|
39 |
+
// serialized string data
|
40 |
+
if ($raw instanceof self) {
|
41 |
+
static::$instance = $raw;
|
42 |
+
} else {
|
43 |
+
static::$instance = is_string($raw) ? static::unserialize($raw) : new self();
|
44 |
+
}
|
45 |
+
}
|
46 |
+
|
47 |
+
return static::$instance;
|
48 |
+
}
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Sync this object data with cache
|
52 |
+
*/
|
53 |
+
public function sync()
|
54 |
+
{
|
55 |
+
update_option(self::OPTIONS_KEY, serialize($this));
|
56 |
+
}
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Sync this instance data with cache
|
60 |
+
*/
|
61 |
+
public static function syncData()
|
62 |
+
{
|
63 |
+
static::instance()->sync();
|
64 |
+
}
|
65 |
+
|
66 |
+
/**
|
67 |
+
* Find plugin account data or create fresh one
|
68 |
+
*
|
69 |
+
* @param Account $account
|
70 |
+
* @return AccountData|null
|
71 |
+
*/
|
72 |
+
public function resolvePluginAccountData(Account $account)
|
73 |
+
{
|
74 |
+
$accountData = $this->findAccountDataById($account->getId());
|
75 |
+
|
76 |
+
if (!$accountData) {
|
77 |
+
$accountData = new AccountData();
|
78 |
+
|
79 |
+
// Set proper default values
|
80 |
+
$accountData->setPath($account->getPath());
|
81 |
+
$accountData->setId($account->getId());
|
82 |
+
$accountData->setSecret($account->getClientSecret());
|
83 |
+
|
84 |
+
array_push($this->accounts, $accountData);
|
85 |
+
}
|
86 |
+
|
87 |
+
return $accountData;
|
88 |
+
}
|
89 |
+
|
90 |
+
/**
|
91 |
+
* Should return account data by base path
|
92 |
+
*
|
93 |
+
* @param $basePath
|
94 |
+
* @return AccountData
|
95 |
+
*/
|
96 |
+
public function getAccountDataByBasePath($basePath)
|
97 |
+
{
|
98 |
+
foreach ($this->accounts as $iterable) {
|
99 |
+
$iterableBasePath = plugin_basename($iterable->getPath());
|
100 |
+
|
101 |
+
if ($iterableBasePath === $basePath) {
|
102 |
+
return $iterable;
|
103 |
+
}
|
104 |
+
}
|
105 |
+
|
106 |
+
return null;
|
107 |
+
}
|
108 |
+
|
109 |
+
/**
|
110 |
+
* Return account by id
|
111 |
+
*
|
112 |
+
* @param $id
|
113 |
+
* @return AccountData|null
|
114 |
+
*/
|
115 |
+
private function findAccountDataById($id)
|
116 |
+
{
|
117 |
+
foreach ($this->accounts as &$iterable) {
|
118 |
+
if ($iterable->getId() === $id) {
|
119 |
+
return $iterable;
|
120 |
+
}
|
121 |
+
}
|
122 |
+
|
123 |
+
return null;
|
124 |
+
}
|
125 |
+
}
|
analyst/src/Analyst.php
ADDED
@@ -0,0 +1,167 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Analyst;
|
3 |
+
|
4 |
+
use Account\Account;
|
5 |
+
use Account\AccountDataFactory;
|
6 |
+
use Analyst\Contracts\AnalystContract;
|
7 |
+
use Analyst\Contracts\RequestorContract;
|
8 |
+
|
9 |
+
class Analyst implements AnalystContract
|
10 |
+
{
|
11 |
+
/**
|
12 |
+
* All plugin's accounts
|
13 |
+
*
|
14 |
+
* @var array
|
15 |
+
*/
|
16 |
+
protected $accounts = array();
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @var Mutator
|
20 |
+
*/
|
21 |
+
protected $mutator;
|
22 |
+
|
23 |
+
/**
|
24 |
+
* @var AccountDataFactory
|
25 |
+
*/
|
26 |
+
protected $accountDataFactory;
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Base url to api
|
30 |
+
*
|
31 |
+
* @var string
|
32 |
+
*/
|
33 |
+
protected $apiBase = 'https://feedback.sellcodes.com/api/v1';
|
34 |
+
|
35 |
+
/**
|
36 |
+
* @var Collector
|
37 |
+
*/
|
38 |
+
protected $collector;
|
39 |
+
|
40 |
+
/**
|
41 |
+
* Singleton instance
|
42 |
+
*
|
43 |
+
* @var static
|
44 |
+
*/
|
45 |
+
protected static $instance;
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Get instance of analyst
|
49 |
+
*
|
50 |
+
* @return Analyst
|
51 |
+
* @throws \Exception
|
52 |
+
*/
|
53 |
+
public static function getInstance()
|
54 |
+
{
|
55 |
+
if (!static::$instance) {
|
56 |
+
static::$instance = new Analyst();
|
57 |
+
}
|
58 |
+
|
59 |
+
return static::$instance;
|
60 |
+
}
|
61 |
+
|
62 |
+
protected function __construct()
|
63 |
+
{
|
64 |
+
$this->mutator = new Mutator();
|
65 |
+
|
66 |
+
$this->accountDataFactory = AccountDataFactory::instance();
|
67 |
+
|
68 |
+
$this->mutator->initialize();
|
69 |
+
|
70 |
+
$this->collector = new Collector($this);
|
71 |
+
|
72 |
+
$this->initialize();
|
73 |
+
}
|
74 |
+
|
75 |
+
/**
|
76 |
+
* Initialize rest of application
|
77 |
+
*/
|
78 |
+
public function initialize()
|
79 |
+
{
|
80 |
+
add_action('init', function () {
|
81 |
+
$this->collector->loadCurrentUser();
|
82 |
+
});
|
83 |
+
}
|
84 |
+
|
85 |
+
/**
|
86 |
+
* Register new account
|
87 |
+
*
|
88 |
+
* @param Account $account
|
89 |
+
* @return Analyst
|
90 |
+
* @throws \Exception
|
91 |
+
*/
|
92 |
+
public function registerAccount($account)
|
93 |
+
{
|
94 |
+
// Stop propagation when account is already registered
|
95 |
+
if ($this->isAccountRegistered($account)) {
|
96 |
+
return $this;
|
97 |
+
}
|
98 |
+
|
99 |
+
// Resolve account data from factory
|
100 |
+
$accountData = $this->accountDataFactory->resolvePluginAccountData($account);
|
101 |
+
|
102 |
+
$account->setData($accountData);
|
103 |
+
|
104 |
+
$account->setRequestor(
|
105 |
+
$this->resolveRequestorForAccount($account)
|
106 |
+
);
|
107 |
+
|
108 |
+
$account->setCollector($this->collector);
|
109 |
+
|
110 |
+
$account->registerHooks();
|
111 |
+
|
112 |
+
$this->accounts[$account->getId()] = $account;
|
113 |
+
|
114 |
+
return $this;
|
115 |
+
}
|
116 |
+
|
117 |
+
/**
|
118 |
+
* Must return version of analyst
|
119 |
+
*
|
120 |
+
* @return string
|
121 |
+
*/
|
122 |
+
public static function version()
|
123 |
+
{
|
124 |
+
$version = require __DIR__ . '/../version.php';
|
125 |
+
|
126 |
+
return $version['sdk'];
|
127 |
+
}
|
128 |
+
|
129 |
+
/**
|
130 |
+
* Is this account registered
|
131 |
+
*
|
132 |
+
* @param Account $account
|
133 |
+
* @return bool
|
134 |
+
*/
|
135 |
+
protected function isAccountRegistered($account)
|
136 |
+
{
|
137 |
+
return isset($this->accounts[$account->getId()]);
|
138 |
+
}
|
139 |
+
|
140 |
+
/**
|
141 |
+
* Resolves requestor for account
|
142 |
+
*
|
143 |
+
* @param Account $account
|
144 |
+
* @return RequestorContract
|
145 |
+
* @throws \Exception
|
146 |
+
*/
|
147 |
+
protected function resolveRequestorForAccount(Account $account)
|
148 |
+
{
|
149 |
+
$requestor = new ApiRequestor($account->getId(), $account->getClientSecret(), $this->apiBase);
|
150 |
+
|
151 |
+
// Set SDK version
|
152 |
+
$requestor->setDefaultHeader(
|
153 |
+
'x-analyst-client-user-agent',
|
154 |
+
sprintf('Analyst/%s', $this->version())
|
155 |
+
);
|
156 |
+
|
157 |
+
return $requestor;
|
158 |
+
}
|
159 |
+
|
160 |
+
/**
|
161 |
+
* @return string
|
162 |
+
*/
|
163 |
+
public function getApiBase()
|
164 |
+
{
|
165 |
+
return $this->apiBase;
|
166 |
+
}
|
167 |
+
}
|
analyst/src/ApiRequestor.php
ADDED
@@ -0,0 +1,257 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst;
|
4 |
+
|
5 |
+
use Exception;
|
6 |
+
use Analyst\Contracts\HttpClientContract;
|
7 |
+
use Analyst\Contracts\RequestorContract;
|
8 |
+
|
9 |
+
class ApiRequestor implements RequestorContract
|
10 |
+
{
|
11 |
+
/**
|
12 |
+
* Supported http client
|
13 |
+
*
|
14 |
+
* @var HttpClientContract
|
15 |
+
*/
|
16 |
+
protected $httpClient;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @var string
|
20 |
+
*/
|
21 |
+
protected $clientId;
|
22 |
+
|
23 |
+
/**
|
24 |
+
* @var string
|
25 |
+
*/
|
26 |
+
protected $clientSecret;
|
27 |
+
|
28 |
+
/**
|
29 |
+
* @var string
|
30 |
+
*/
|
31 |
+
protected $apiBase;
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Default headers to be sent
|
35 |
+
*
|
36 |
+
* @var array
|
37 |
+
*/
|
38 |
+
protected $defaultHeaders = [
|
39 |
+
'accept' => 'application/json',
|
40 |
+
'content-type' => 'application/json'
|
41 |
+
];
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Prioritized http clients
|
45 |
+
*
|
46 |
+
* @var array
|
47 |
+
*/
|
48 |
+
protected $availableClients = [
|
49 |
+
'Analyst\Http\WordPressHttpClient',
|
50 |
+
'Analyst\Http\CurlHttpClient',
|
51 |
+
'Analyst\Http\DummyHttpClient',
|
52 |
+
];
|
53 |
+
|
54 |
+
/**
|
55 |
+
* ApiRequestor constructor.
|
56 |
+
* @param $id
|
57 |
+
* @param $secret
|
58 |
+
* @param $apiBase
|
59 |
+
* @throws \Exception
|
60 |
+
*/
|
61 |
+
public function __construct($id, $secret, $apiBase)
|
62 |
+
{
|
63 |
+
$this->clientId = $id;
|
64 |
+
$this->clientSecret = $secret;
|
65 |
+
|
66 |
+
$this->setApiBase($apiBase);
|
67 |
+
|
68 |
+
$this->httpClient = $this->resolveHttpClient();
|
69 |
+
}
|
70 |
+
|
71 |
+
/**
|
72 |
+
* Set api base url
|
73 |
+
*
|
74 |
+
* @param $url
|
75 |
+
*/
|
76 |
+
public function setApiBase($url)
|
77 |
+
{
|
78 |
+
$this->apiBase = $url;
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Get request
|
83 |
+
*
|
84 |
+
* @param $url
|
85 |
+
* @param array $headers
|
86 |
+
* @return mixed
|
87 |
+
*/
|
88 |
+
public function get($url, $headers = [])
|
89 |
+
{
|
90 |
+
return $this->request('GET', $url, null, $headers);
|
91 |
+
}
|
92 |
+
|
93 |
+
/**
|
94 |
+
* Post request
|
95 |
+
*
|
96 |
+
* @param $url
|
97 |
+
* @param $body
|
98 |
+
* @param array $headers
|
99 |
+
* @return mixed
|
100 |
+
*/
|
101 |
+
public function post($url, $body = [], $headers = [])
|
102 |
+
{
|
103 |
+
return $this->request('POST', $url, $body, $headers);
|
104 |
+
}
|
105 |
+
|
106 |
+
/**
|
107 |
+
* Put request
|
108 |
+
*
|
109 |
+
* @param $url
|
110 |
+
* @param $body
|
111 |
+
* @param array $headers
|
112 |
+
* @return mixed
|
113 |
+
*/
|
114 |
+
public function put($url, $body = [], $headers = [])
|
115 |
+
{
|
116 |
+
return $this->request('PUT', $url, $body, $headers);
|
117 |
+
}
|
118 |
+
|
119 |
+
/**
|
120 |
+
* Delete request
|
121 |
+
*
|
122 |
+
* @param $url
|
123 |
+
* @param array $headers
|
124 |
+
* @return mixed
|
125 |
+
*/
|
126 |
+
public function delete($url, $headers = [])
|
127 |
+
{
|
128 |
+
return $this->request('DELETE', $url, null, $headers);
|
129 |
+
}
|
130 |
+
|
131 |
+
/**
|
132 |
+
* Make request to api
|
133 |
+
*
|
134 |
+
* @param $method
|
135 |
+
* @param $url
|
136 |
+
* @param array $body
|
137 |
+
* @param array $headers
|
138 |
+
* @return mixed
|
139 |
+
*/
|
140 |
+
protected function request($method, $url, $body = [], $headers = [])
|
141 |
+
{
|
142 |
+
$fullUrl = $this->resolveFullUrl($url);
|
143 |
+
|
144 |
+
$date = date('r', time());
|
145 |
+
|
146 |
+
$headers['date'] = $date;
|
147 |
+
$headers['signature'] = $this->resolveSignature($this->clientSecret, $method, $fullUrl, $body, $date);
|
148 |
+
|
149 |
+
// Lowercase header names
|
150 |
+
$headers = $this->prepareHeaders(
|
151 |
+
array_merge($headers, $this->defaultHeaders)
|
152 |
+
);
|
153 |
+
|
154 |
+
$response = $this->httpClient->request($method, $fullUrl, $body, $headers);
|
155 |
+
|
156 |
+
// TODO: Check response code and take actions
|
157 |
+
|
158 |
+
return $response;
|
159 |
+
}
|
160 |
+
|
161 |
+
/**
|
162 |
+
* Set one default header
|
163 |
+
*
|
164 |
+
* @param $header
|
165 |
+
* @param $value
|
166 |
+
*/
|
167 |
+
public function setDefaultHeader($header, $value)
|
168 |
+
{
|
169 |
+
$this->defaultHeaders[
|
170 |
+
$this->resolveValidHeaderName($header)
|
171 |
+
] = $value;
|
172 |
+
}
|
173 |
+
|
174 |
+
/**
|
175 |
+
* Resolves supported http client
|
176 |
+
*
|
177 |
+
* @return HttpClientContract
|
178 |
+
* @throws Exception
|
179 |
+
*/
|
180 |
+
protected function resolveHttpClient()
|
181 |
+
{
|
182 |
+
$clients = array_filter($this->availableClients, $this->guessClientSupportEnvironment());
|
183 |
+
|
184 |
+
if (!isset($clients[0])) {
|
185 |
+
throw new Exception('There is no http client which this application can support');
|
186 |
+
}
|
187 |
+
|
188 |
+
// Instantiate first supported http client
|
189 |
+
return new $clients[0];
|
190 |
+
}
|
191 |
+
|
192 |
+
/**
|
193 |
+
* This will filter out clients which is not supported
|
194 |
+
* by the current environment
|
195 |
+
*
|
196 |
+
* @return \Closure
|
197 |
+
*/
|
198 |
+
protected function guessClientSupportEnvironment()
|
199 |
+
{
|
200 |
+
return function ($client) {
|
201 |
+
return forward_static_call([$client, 'hasSupport']);
|
202 |
+
};
|
203 |
+
}
|
204 |
+
|
205 |
+
/**
|
206 |
+
* Resolves valid header name
|
207 |
+
*
|
208 |
+
* @param $headerName
|
209 |
+
* @return string
|
210 |
+
*/
|
211 |
+
private function resolveValidHeaderName($headerName)
|
212 |
+
{
|
213 |
+
return strtolower($headerName);
|
214 |
+
}
|
215 |
+
|
216 |
+
/**
|
217 |
+
* Lowercase header names
|
218 |
+
*
|
219 |
+
* @param $headers
|
220 |
+
* @return array
|
221 |
+
*/
|
222 |
+
private function prepareHeaders($headers)
|
223 |
+
{
|
224 |
+
return array_change_key_case($headers, CASE_LOWER);
|
225 |
+
}
|
226 |
+
|
227 |
+
/**
|
228 |
+
* Sign request
|
229 |
+
*
|
230 |
+
* @param $key
|
231 |
+
* @param $method
|
232 |
+
* @param $url
|
233 |
+
* @param $body
|
234 |
+
* @param $date
|
235 |
+
*
|
236 |
+
* @return false|string
|
237 |
+
*/
|
238 |
+
private function resolveSignature($key, $method, $url, $body, $date)
|
239 |
+
{
|
240 |
+
$string = implode('\n', [$method, $url, md5(json_encode($body)), $date]);
|
241 |
+
|
242 |
+
$contentSecret = hash_hmac('sha256', $string, $key);
|
243 |
+
|
244 |
+
return sprintf('%s:%s', $this->clientId, $contentSecret);
|
245 |
+
}
|
246 |
+
|
247 |
+
/**
|
248 |
+
* Compose full url
|
249 |
+
*
|
250 |
+
* @param $url
|
251 |
+
* @return string
|
252 |
+
*/
|
253 |
+
private function resolveFullUrl($url)
|
254 |
+
{
|
255 |
+
return sprintf('%s/%s', $this->apiBase, trim($url, '/'));
|
256 |
+
}
|
257 |
+
}
|
analyst/src/ApiResponse.php
ADDED
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst;
|
4 |
+
|
5 |
+
class ApiResponse
|
6 |
+
{
|
7 |
+
/**
|
8 |
+
* Response headers
|
9 |
+
*
|
10 |
+
* @var array
|
11 |
+
*/
|
12 |
+
public $headers;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Response body
|
16 |
+
*
|
17 |
+
* @var mixed
|
18 |
+
*/
|
19 |
+
public $body;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Status code
|
23 |
+
*
|
24 |
+
* @var string
|
25 |
+
*/
|
26 |
+
public $code;
|
27 |
+
|
28 |
+
public function __construct($body, $code, $headers)
|
29 |
+
{
|
30 |
+
$this->body = $body;
|
31 |
+
$this->code = $code;
|
32 |
+
$this->headers = $headers;
|
33 |
+
}
|
34 |
+
|
35 |
+
/**
|
36 |
+
* Whether status code is successful
|
37 |
+
*
|
38 |
+
* @return bool
|
39 |
+
*/
|
40 |
+
public function isSuccess()
|
41 |
+
{
|
42 |
+
return $this->code >= 200 && $this->code < 300;
|
43 |
+
}
|
44 |
+
}
|
analyst/src/Cache/DatabaseCache.php
ADDED
@@ -0,0 +1,127 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst\Cache;
|
4 |
+
|
5 |
+
use Analyst\Contracts\CacheContract;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Class DatabaseCache
|
9 |
+
*
|
10 |
+
* @since 1.1.5
|
11 |
+
*/
|
12 |
+
class DatabaseCache implements CacheContract
|
13 |
+
{
|
14 |
+
const OPTION_KEY = 'analyst_cache';
|
15 |
+
|
16 |
+
protected static $instance;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Get instance of db cache
|
20 |
+
*
|
21 |
+
* @return DatabaseCache
|
22 |
+
*/
|
23 |
+
public static function getInstance()
|
24 |
+
{
|
25 |
+
if (!self::$instance) {
|
26 |
+
self::$instance = new DatabaseCache();
|
27 |
+
}
|
28 |
+
|
29 |
+
return self::$instance;
|
30 |
+
}
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Key value pair
|
34 |
+
*
|
35 |
+
* @var array[]
|
36 |
+
*/
|
37 |
+
protected $values = [];
|
38 |
+
|
39 |
+
/**
|
40 |
+
* DatabaseCache constructor.
|
41 |
+
*/
|
42 |
+
public function __construct()
|
43 |
+
{
|
44 |
+
$raw = get_option(self::OPTION_KEY, serialize([]));
|
45 |
+
|
46 |
+
// Raw data may be an array already
|
47 |
+
$this->values = is_array($raw) ? $raw : @unserialize($raw);
|
48 |
+
|
49 |
+
// In case serialization is failed
|
50 |
+
// make sure values is an array
|
51 |
+
if (!is_array($this->values)) {
|
52 |
+
$this->values = [];
|
53 |
+
}
|
54 |
+
}
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Save value with given key
|
58 |
+
*
|
59 |
+
* @param string $key
|
60 |
+
* @param string $value
|
61 |
+
*
|
62 |
+
* @return static
|
63 |
+
*/
|
64 |
+
public function put($key, $value)
|
65 |
+
{
|
66 |
+
$this->values[$key] = $value;
|
67 |
+
|
68 |
+
$this->sync();
|
69 |
+
|
70 |
+
return $this;
|
71 |
+
}
|
72 |
+
|
73 |
+
/**
|
74 |
+
* Get value by given key
|
75 |
+
*
|
76 |
+
* @param $key
|
77 |
+
*
|
78 |
+
* @param null $default
|
79 |
+
* @return string
|
80 |
+
*/
|
81 |
+
public function get($key, $default = null)
|
82 |
+
{
|
83 |
+
$value = isset($this->values[$key]) ? $this->values[$key] : $default;
|
84 |
+
|
85 |
+
return $value;
|
86 |
+
}
|
87 |
+
|
88 |
+
/**
|
89 |
+
* @param $key
|
90 |
+
*
|
91 |
+
* @return static
|
92 |
+
*/
|
93 |
+
public function delete($key)
|
94 |
+
{
|
95 |
+
if (isset($this->values[$key])) {
|
96 |
+
unset($this->values[$key]);
|
97 |
+
|
98 |
+
$this->sync();
|
99 |
+
}
|
100 |
+
|
101 |
+
return $this;
|
102 |
+
}
|
103 |
+
|
104 |
+
/**
|
105 |
+
* Update cache in DB
|
106 |
+
*/
|
107 |
+
protected function sync()
|
108 |
+
{
|
109 |
+
update_option(self::OPTION_KEY, serialize($this->values));
|
110 |
+
}
|
111 |
+
|
112 |
+
/**
|
113 |
+
* Should get value and remove it from cache
|
114 |
+
*
|
115 |
+
* @param $key
|
116 |
+
* @param null $default
|
117 |
+
* @return mixed
|
118 |
+
*/
|
119 |
+
public function pop($key, $default = null)
|
120 |
+
{
|
121 |
+
$value = $this->get($key);
|
122 |
+
|
123 |
+
$this->delete($key);
|
124 |
+
|
125 |
+
return $value;
|
126 |
+
}
|
127 |
+
}
|
analyst/src/Collector.php
ADDED
@@ -0,0 +1,217 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst;
|
4 |
+
|
5 |
+
use Analyst\Contracts\AnalystContract;
|
6 |
+
|
7 |
+
/**
|
8 |
+
* Class Collector is a set of getters
|
9 |
+
* to retrieve some data from wp site
|
10 |
+
*/
|
11 |
+
class Collector
|
12 |
+
{
|
13 |
+
/**
|
14 |
+
* @var AnalystContract
|
15 |
+
*/
|
16 |
+
protected $sdk;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @var \WP_User
|
20 |
+
*/
|
21 |
+
protected $user;
|
22 |
+
|
23 |
+
public function __construct(AnalystContract $sdk)
|
24 |
+
{
|
25 |
+
$this->sdk = $sdk;
|
26 |
+
}
|
27 |
+
|
28 |
+
/**
|
29 |
+
* Load current user into memory
|
30 |
+
*/
|
31 |
+
public function loadCurrentUser()
|
32 |
+
{
|
33 |
+
$this->user = wp_get_current_user();
|
34 |
+
}
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Get site url
|
38 |
+
*
|
39 |
+
* @return string
|
40 |
+
*/
|
41 |
+
public function getSiteUrl()
|
42 |
+
{
|
43 |
+
return get_option('siteurl');
|
44 |
+
}
|
45 |
+
|
46 |
+
/**
|
47 |
+
* Get current user email
|
48 |
+
*
|
49 |
+
* @return string
|
50 |
+
*/
|
51 |
+
public function getCurrentUserEmail()
|
52 |
+
{
|
53 |
+
return $this->user->user_email;
|
54 |
+
}
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Get's email from general settings
|
58 |
+
*
|
59 |
+
* @return string
|
60 |
+
*/
|
61 |
+
public function getGeneralEmailAddress()
|
62 |
+
{
|
63 |
+
return get_option('admin_email');
|
64 |
+
}
|
65 |
+
|
66 |
+
/**
|
67 |
+
* Is this user administrator
|
68 |
+
*
|
69 |
+
* @return bool
|
70 |
+
*/
|
71 |
+
public function isUserAdministrator()
|
72 |
+
{
|
73 |
+
return in_array('administrator', $this->user->roles);
|
74 |
+
}
|
75 |
+
|
76 |
+
/**
|
77 |
+
* User name
|
78 |
+
*
|
79 |
+
* @return string
|
80 |
+
*/
|
81 |
+
public function getCurrentUserName()
|
82 |
+
{
|
83 |
+
return $this->user ? $this->user->user_nicename : 'unknown';
|
84 |
+
}
|
85 |
+
|
86 |
+
/**
|
87 |
+
* WP version
|
88 |
+
*
|
89 |
+
* @return string
|
90 |
+
*/
|
91 |
+
public function getWordPressVersion()
|
92 |
+
{
|
93 |
+
global $wp_version;
|
94 |
+
|
95 |
+
return $wp_version;
|
96 |
+
}
|
97 |
+
|
98 |
+
/**
|
99 |
+
* PHP version
|
100 |
+
*
|
101 |
+
* @return string
|
102 |
+
*/
|
103 |
+
public function getPHPVersion()
|
104 |
+
{
|
105 |
+
return phpversion();
|
106 |
+
}
|
107 |
+
|
108 |
+
/**
|
109 |
+
* Resolves plugin information
|
110 |
+
*
|
111 |
+
* @param string $path Absolute path to plugin
|
112 |
+
* @return array
|
113 |
+
*/
|
114 |
+
public function resolvePluginData($path)
|
115 |
+
{
|
116 |
+
if( !function_exists('get_plugin_data') ){
|
117 |
+
require_once( ABSPATH . 'wp-admin/includes/plugin.php' );
|
118 |
+
}
|
119 |
+
|
120 |
+
return get_plugin_data($path);
|
121 |
+
}
|
122 |
+
|
123 |
+
/**
|
124 |
+
* Get plugin name by path
|
125 |
+
*
|
126 |
+
* @param $path
|
127 |
+
* @return string
|
128 |
+
*/
|
129 |
+
public function getPluginName($path)
|
130 |
+
{
|
131 |
+
$data = $this->resolvePluginData($path);
|
132 |
+
|
133 |
+
return $data['Name'];
|
134 |
+
}
|
135 |
+
|
136 |
+
/**
|
137 |
+
* Get plugin version
|
138 |
+
*
|
139 |
+
* @param $path
|
140 |
+
* @return string
|
141 |
+
*/
|
142 |
+
public function getPluginVersion($path)
|
143 |
+
{
|
144 |
+
$data = $this->resolvePluginData($path);
|
145 |
+
|
146 |
+
return $data['Version'] ? $data['Version'] : null;
|
147 |
+
}
|
148 |
+
|
149 |
+
/**
|
150 |
+
* Get server ip
|
151 |
+
*
|
152 |
+
* @return string
|
153 |
+
*/
|
154 |
+
public function getServerIp()
|
155 |
+
{
|
156 |
+
return $_SERVER['SERVER_ADDR'];
|
157 |
+
}
|
158 |
+
|
159 |
+
/**
|
160 |
+
* @return string
|
161 |
+
*/
|
162 |
+
public function getSDKVersion()
|
163 |
+
{
|
164 |
+
return $this->sdk->version();
|
165 |
+
}
|
166 |
+
|
167 |
+
/**
|
168 |
+
* @return string
|
169 |
+
*/
|
170 |
+
public function getMysqlVersion()
|
171 |
+
{
|
172 |
+
$conn = mysqli_connect(DB_HOST, DB_USER, DB_PASSWORD, DB_NAME);
|
173 |
+
|
174 |
+
$version = mysqli_get_server_info($conn);
|
175 |
+
|
176 |
+
return $version ? $version : 'unknown';
|
177 |
+
}
|
178 |
+
|
179 |
+
/**
|
180 |
+
* @return string
|
181 |
+
*/
|
182 |
+
public function getSiteLanguage()
|
183 |
+
{
|
184 |
+
return get_locale();
|
185 |
+
}
|
186 |
+
|
187 |
+
|
188 |
+
/**
|
189 |
+
* Current WP theme
|
190 |
+
*
|
191 |
+
* @return false|string
|
192 |
+
*/
|
193 |
+
public function getCurrentThemeName()
|
194 |
+
{
|
195 |
+
return wp_get_theme()->get('Name');
|
196 |
+
}
|
197 |
+
|
198 |
+
/**
|
199 |
+
* Get active plugins list
|
200 |
+
*
|
201 |
+
* @return array
|
202 |
+
*/
|
203 |
+
public function getActivePluginsList()
|
204 |
+
{
|
205 |
+
if (!function_exists('get_plugins')) {
|
206 |
+
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
207 |
+
}
|
208 |
+
|
209 |
+
$allPlugins = get_plugins();
|
210 |
+
|
211 |
+
$activePluginsNames = array_map(function ($path) use ($allPlugins) {
|
212 |
+
return $allPlugins[$path]['Name'];
|
213 |
+
}, get_option('active_plugins'));
|
214 |
+
|
215 |
+
return $activePluginsNames;
|
216 |
+
}
|
217 |
+
}
|
analyst/src/Contracts/AnalystContract.php
ADDED
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Analyst\Contracts;
|
3 |
+
|
4 |
+
interface AnalystContract
|
5 |
+
{
|
6 |
+
/**
|
7 |
+
* Must return version of analyst
|
8 |
+
*
|
9 |
+
* @return string
|
10 |
+
*/
|
11 |
+
public static function version();
|
12 |
+
}
|
analyst/src/Contracts/CacheContract.php
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst\Contracts;
|
4 |
+
|
5 |
+
/**
|
6 |
+
* Interface CacheContract
|
7 |
+
*
|
8 |
+
* @since 1.1.5
|
9 |
+
*/
|
10 |
+
interface CacheContract
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Save value with given key
|
14 |
+
*
|
15 |
+
* @param string $key
|
16 |
+
* @param string $value
|
17 |
+
*
|
18 |
+
* @return static
|
19 |
+
*/
|
20 |
+
public function put($key, $value);
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Get value by given key
|
24 |
+
*
|
25 |
+
* @param $key
|
26 |
+
*
|
27 |
+
* @param null $default
|
28 |
+
* @return string
|
29 |
+
*/
|
30 |
+
public function get($key, $default = null);
|
31 |
+
|
32 |
+
/**
|
33 |
+
* @param $key
|
34 |
+
*
|
35 |
+
* @return static
|
36 |
+
*/
|
37 |
+
public function delete($key);
|
38 |
+
|
39 |
+
/**
|
40 |
+
* Should get value and remove it from cache
|
41 |
+
*
|
42 |
+
* @param $key
|
43 |
+
* @param null $default
|
44 |
+
* @return mixed
|
45 |
+
*/
|
46 |
+
public function pop($key, $default = null);
|
47 |
+
}
|
analyst/src/Contracts/HttpClientContract.php
ADDED
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
namespace Analyst\Contracts;
|
3 |
+
|
4 |
+
use Analyst\ApiResponse;
|
5 |
+
|
6 |
+
interface HttpClientContract
|
7 |
+
{
|
8 |
+
/**
|
9 |
+
* Make an http request
|
10 |
+
*
|
11 |
+
* @param $method
|
12 |
+
* @param $url
|
13 |
+
* @param $body
|
14 |
+
* @param $headers
|
15 |
+
* @return ApiResponse
|
16 |
+
*/
|
17 |
+
public function request($method, $url, $body, $headers);
|
18 |
+
|
19 |
+
/**
|
20 |
+
* Must return `true` if client is supported
|
21 |
+
*
|
22 |
+
* @return bool
|
23 |
+
*/
|
24 |
+
public static function hasSupport();
|
25 |
+
}
|
analyst/src/Contracts/RequestContract.php
ADDED
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst\Contracts;
|
4 |
+
|
5 |
+
use Analyst\ApiResponse;
|
6 |
+
|
7 |
+
interface RequestContract
|
8 |
+
{
|
9 |
+
/**
|
10 |
+
* Cast request data to array
|
11 |
+
*
|
12 |
+
* @return array
|
13 |
+
*/
|
14 |
+
public function toArray();
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Execute the request
|
18 |
+
* @param RequestorContract $requestor
|
19 |
+
* @return ApiResponse
|
20 |
+
*/
|
21 |
+
public function execute(RequestorContract $requestor);
|
22 |
+
}
|
analyst/src/Contracts/RequestorContract.php
ADDED
@@ -0,0 +1,44 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst\Contracts;
|
4 |
+
|
5 |
+
interface RequestorContract
|
6 |
+
{
|
7 |
+
/**
|
8 |
+
* Get request
|
9 |
+
*
|
10 |
+
* @param $url
|
11 |
+
* @param array $headers
|
12 |
+
* @return mixed
|
13 |
+
*/
|
14 |
+
public function get($url, $headers = []);
|
15 |
+
|
16 |
+
/**
|
17 |
+
* Post request
|
18 |
+
*
|
19 |
+
* @param $url
|
20 |
+
* @param $body
|
21 |
+
* @param array $headers
|
22 |
+
* @return mixed
|
23 |
+
*/
|
24 |
+
public function post($url, $body = [], $headers = []);
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Put request
|
28 |
+
*
|
29 |
+
* @param $url
|
30 |
+
* @param $body
|
31 |
+
* @param array $headers
|
32 |
+
* @return mixed
|
33 |
+
*/
|
34 |
+
public function put($url, $body = [], $headers = []);
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Delete request
|
38 |
+
*
|
39 |
+
* @param $url
|
40 |
+
* @param array $headers
|
41 |
+
* @return mixed
|
42 |
+
*/
|
43 |
+
public function delete($url, $headers = []);
|
44 |
+
}
|
analyst/src/Contracts/TrackerContract.php
ADDED
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst\Contracts;
|
4 |
+
|
5 |
+
interface TrackerContract
|
6 |
+
{
|
7 |
+
/**
|
8 |
+
* Should register activation and deactivation
|
9 |
+
* event hooks
|
10 |
+
*
|
11 |
+
* @return void
|
12 |
+
*/
|
13 |
+
public function registerHooks();
|
14 |
+
|
15 |
+
/**
|
16 |
+
* Will fire when admin activates plugin
|
17 |
+
*
|
18 |
+
* @return void
|
19 |
+
*/
|
20 |
+
public function onActivePluginListener();
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Will fire when admin deactivates plugin
|
24 |
+
*
|
25 |
+
* @return void
|
26 |
+
*/
|
27 |
+
public function onDeactivatePluginListener();
|
28 |
+
|
29 |
+
/**
|
30 |
+
* Will fire when user opted in
|
31 |
+
*
|
32 |
+
* @return void
|
33 |
+
*/
|
34 |
+
public function onOptInListener();
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Will fire when user opted out
|
38 |
+
*
|
39 |
+
* @return void
|
40 |
+
*/
|
41 |
+
public function onOptOutListener();
|
42 |
+
|
43 |
+
/**
|
44 |
+
* Will fire when user accept opt/in at first time
|
45 |
+
*
|
46 |
+
* @return void
|
47 |
+
*/
|
48 |
+
public function onInstallListener();
|
49 |
+
|
50 |
+
/**
|
51 |
+
* Will fire when user skipped installation
|
52 |
+
*
|
53 |
+
* @return void
|
54 |
+
*/
|
55 |
+
public function onSkipInstallListener();
|
56 |
+
|
57 |
+
/**
|
58 |
+
* Will fire when user delete plugin through admin panel.
|
59 |
+
* This action will happen if admin at least once
|
60 |
+
* activated the plugin.
|
61 |
+
*
|
62 |
+
* The register_uninstall_hook function accepts only static
|
63 |
+
* function or global function to be executed, so this is
|
64 |
+
* why this method is static
|
65 |
+
*
|
66 |
+
* @return void
|
67 |
+
*/
|
68 |
+
public static function onUninstallPluginListener();
|
69 |
+
}
|
analyst/src/Core/AbstractFactory.php
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst\Core;
|
4 |
+
|
5 |
+
abstract class AbstractFactory
|
6 |
+
{
|
7 |
+
/**
|
8 |
+
* Unserialize to static::class instance
|
9 |
+
*
|
10 |
+
* @param $raw
|
11 |
+
* @return static
|
12 |
+
*/
|
13 |
+
protected static function unserialize($raw)
|
14 |
+
{
|
15 |
+
$instance = @unserialize($raw);
|
16 |
+
|
17 |
+
$isProperObject = is_object($instance) && $instance instanceof static;
|
18 |
+
|
19 |
+
// In case for some reason unserialized object is not
|
20 |
+
// static::class we make sure it is static::class
|
21 |
+
if (!$isProperObject) {
|
22 |
+
$instance = new static();
|
23 |
+
}
|
24 |
+
|
25 |
+
return $instance;
|
26 |
+
}
|
27 |
+
}
|
analyst/src/Http/CurlHttpClient.php
ADDED
@@ -0,0 +1,102 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst\Http;
|
4 |
+
|
5 |
+
use Analyst\ApiResponse;
|
6 |
+
use Analyst\Contracts\HttpClientContract;
|
7 |
+
|
8 |
+
class CurlHttpClient implements HttpClientContract
|
9 |
+
{
|
10 |
+
/**
|
11 |
+
* Make an http request
|
12 |
+
*
|
13 |
+
* @param $method
|
14 |
+
* @param $url
|
15 |
+
* @param array $body
|
16 |
+
* @param $headers
|
17 |
+
* @return mixed
|
18 |
+
*/
|
19 |
+
public function request($method, $url, $body, $headers)
|
20 |
+
{
|
21 |
+
$method = strtoupper($method);
|
22 |
+
|
23 |
+
$options = [
|
24 |
+
CURLOPT_RETURNTRANSFER => true,
|
25 |
+
CURLOPT_URL => $url,
|
26 |
+
CURLOPT_HTTPHEADER => $this->prepareRequestHeaders($headers),
|
27 |
+
CURLOPT_CUSTOMREQUEST => $method,
|
28 |
+
CURLOPT_FAILONERROR => true,
|
29 |
+
CURLOPT_HEADER => true,
|
30 |
+
CURLOPT_TIMEOUT => 30,
|
31 |
+
];
|
32 |
+
|
33 |
+
if ($method === 'POST') {
|
34 |
+
$options[CURLOPT_POST] = 1;
|
35 |
+
$options[CURLOPT_POSTFIELDS] = json_encode($body);
|
36 |
+
}
|
37 |
+
|
38 |
+
$curl = curl_init();
|
39 |
+
|
40 |
+
curl_setopt_array($curl, $options);
|
41 |
+
|
42 |
+
$response = curl_exec($curl);
|
43 |
+
|
44 |
+
list($rawHeaders, $rawBody) = explode("\r\n\r\n", $response, 2);
|
45 |
+
|
46 |
+
$info = curl_getinfo($curl);
|
47 |
+
|
48 |
+
curl_close($curl);
|
49 |
+
|
50 |
+
$responseHeaders = $this->resolveResponseHeaders($rawHeaders);
|
51 |
+
$responseBody = json_decode($rawBody, true);
|
52 |
+
|
53 |
+
return new ApiResponse($responseBody, $info['http_code'], $responseHeaders);
|
54 |
+
}
|
55 |
+
|
56 |
+
/**
|
57 |
+
* Must return `true` if client is supported
|
58 |
+
*
|
59 |
+
* @return bool
|
60 |
+
*/
|
61 |
+
public static function hasSupport()
|
62 |
+
{
|
63 |
+
return function_exists('curl_version');
|
64 |
+
}
|
65 |
+
|
66 |
+
/**
|
67 |
+
* Modify request headers from key value pair
|
68 |
+
* to vector array
|
69 |
+
*
|
70 |
+
* @param array $headers
|
71 |
+
* @return array
|
72 |
+
*/
|
73 |
+
protected function prepareRequestHeaders ($headers)
|
74 |
+
{
|
75 |
+
return array_map(function ($key, $value) {
|
76 |
+
return sprintf('%s:%s', $key, $value);
|
77 |
+
}, array_keys($headers), $headers);
|
78 |
+
}
|
79 |
+
|
80 |
+
/**
|
81 |
+
* Resolve raw response headers as
|
82 |
+
* associative array
|
83 |
+
*
|
84 |
+
* @param $rawHeaders
|
85 |
+
* @return array
|
86 |
+
*/
|
87 |
+
private function resolveResponseHeaders($rawHeaders)
|
88 |
+
{
|
89 |
+
$headers = [];
|
90 |
+
|
91 |
+
foreach (explode("\r\n", $rawHeaders) as $i => $line) {
|
92 |
+
$parts = explode(': ', $line);
|
93 |
+
|
94 |
+
if (count($parts) === 1) {
|
95 |
+
continue;
|
96 |
+
}
|
97 |
+
|
98 |
+
$headers[$parts[0]] = $parts[1];
|
99 |
+
}
|
100 |
+
return $headers;
|
101 |
+
}
|
102 |
+
}
|
analyst/src/Http/DummyHttpClient.php
ADDED
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst\Http;
|
4 |
+
|
5 |
+
use Analyst\ApiResponse;
|
6 |
+
use Analyst\Contracts\HttpClientContract;
|
7 |
+
|
8 |
+
class DummyHttpClient implements HttpClientContract
|
9 |
+
{
|
10 |
+
/**
|
11 |
+
* Make an http request
|
12 |
+
*
|
13 |
+
* @param $method
|
14 |
+
* @param $url
|
15 |
+
* @param $body
|
16 |
+
* @param $headers
|
17 |
+
* @return ApiResponse
|
18 |
+
*/
|
19 |
+
public function request($method, $url, $body, $headers)
|
20 |
+
{
|
21 |
+
return new ApiResponse('Dummy response', 200, []);
|
22 |
+
}
|
23 |
+
|
24 |
+
/**
|
25 |
+
* Must return `true` if client is supported
|
26 |
+
*
|
27 |
+
* @return bool
|
28 |
+
*/
|
29 |
+
public static function hasSupport()
|
30 |
+
{
|
31 |
+
return true;
|
32 |
+
}
|
33 |
+
}
|
analyst/src/Http/Requests/AbstractLoggerRequest.php
ADDED
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst\Http\Requests;
|
4 |
+
|
5 |
+
use Analyst\ApiResponse;
|
6 |
+
use Analyst\Collector;
|
7 |
+
use Analyst\Contracts\RequestContract;
|
8 |
+
use Analyst\Contracts\RequestorContract;
|
9 |
+
|
10 |
+
abstract class AbstractLoggerRequest implements RequestContract
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* @var Collector
|
14 |
+
*/
|
15 |
+
protected $collector;
|
16 |
+
|
17 |
+
/**
|
18 |
+
* @var integer
|
19 |
+
*/
|
20 |
+
protected $id;
|
21 |
+
|
22 |
+
/**
|
23 |
+
* @var string
|
24 |
+
*/
|
25 |
+
protected $path;
|
26 |
+
|
27 |
+
public function __construct(Collector $collector, $pluginId, $path)
|
28 |
+
{
|
29 |
+
$this->collector = $collector;
|
30 |
+
$this->id = $pluginId;
|
31 |
+
$this->path = $path;
|
32 |
+
}
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Cast request data to array
|
36 |
+
*
|
37 |
+
* @return array
|
38 |
+
*/
|
39 |
+
public function toArray()
|
40 |
+
{
|
41 |
+
return [
|
42 |
+
'plugin_id' => $this->id,
|
43 |
+
'php_version' => $this->collector->getPHPVersion(),
|
44 |
+
'wp_version' => $this->collector->getWordPressVersion(),
|
45 |
+
'plugin_version' => $this->collector->getPluginVersion($this->path),
|
46 |
+
'url' => $this->collector->getSiteUrl(),
|
47 |
+
'sdk_version' => $this->collector->getSDKVersion(),
|
48 |
+
'ip' => $this->collector->getServerIp(),
|
49 |
+
'mysql_version' => $this->collector->getMysqlVersion(),
|
50 |
+
'locale' => $this->collector->getSiteLanguage(),
|
51 |
+
'current_theme' => $this->collector->getCurrentThemeName(),
|
52 |
+
'active_plugins_list' => implode(', ', $this->collector->getActivePluginsList()),
|
53 |
+
'email' => $this->collector->getGeneralEmailAddress(),
|
54 |
+
'name' => $this->collector->getCurrentUserName()
|
55 |
+
];
|
56 |
+
}
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Execute the request
|
60 |
+
* @param RequestorContract $requestor
|
61 |
+
* @return ApiResponse
|
62 |
+
*/
|
63 |
+
public abstract function execute(RequestorContract $requestor);
|
64 |
+
}
|
analyst/src/Http/Requests/ActivateRequest.php
ADDED
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst\Http\Requests;
|
4 |
+
|
5 |
+
use Analyst\ApiResponse;
|
6 |
+
use Analyst\Collector;
|
7 |
+
use Analyst\Contracts\RequestContract;
|
8 |
+
use Analyst\Contracts\RequestorContract;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Class ActivateRequest
|
12 |
+
*
|
13 |
+
* Is is very similar to install request
|
14 |
+
* but with different path
|
15 |
+
*
|
16 |
+
* @since 0.9.12
|
17 |
+
*/
|
18 |
+
class ActivateRequest extends AbstractLoggerRequest
|
19 |
+
{
|
20 |
+
/**
|
21 |
+
* Execute the request
|
22 |
+
* @param RequestorContract $requestor
|
23 |
+
* @return ApiResponse
|
24 |
+
*/
|
25 |
+
public function execute(RequestorContract $requestor)
|
26 |
+
{
|
27 |
+
return $requestor->post('logger/activate', $this->toArray());
|
28 |
+
}
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Make request instance
|
32 |
+
*
|
33 |
+
* @param Collector $collector
|
34 |
+
* @param $pluginId
|
35 |
+
* @param $path
|
36 |
+
* @return static
|
37 |
+
*/
|
38 |
+
public static function make(Collector $collector, $pluginId, $path)
|
39 |
+
{
|
40 |
+
return new static($collector, $pluginId, $path);
|
41 |
+
}
|
42 |
+
}
|
analyst/src/Http/Requests/DeactivateRequest.php
ADDED
@@ -0,0 +1,64 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst\Http\Requests;
|
4 |
+
|
5 |
+
use Analyst\ApiResponse;
|
6 |
+
use Analyst\Collector;
|
7 |
+
use Analyst\Contracts\RequestorContract;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Class DeactivateRequest
|
11 |
+
*
|
12 |
+
* @since 0.9.10
|
13 |
+
*/
|
14 |
+
class DeactivateRequest extends AbstractLoggerRequest
|
15 |
+
{
|
16 |
+
/**
|
17 |
+
* @var string
|
18 |
+
*/
|
19 |
+
protected $question;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* @var string
|
23 |
+
*/
|
24 |
+
protected $answer;
|
25 |
+
|
26 |
+
/**
|
27 |
+
* @param Collector $collector
|
28 |
+
* @param $pluginId
|
29 |
+
* @param $path
|
30 |
+
* @param $question
|
31 |
+
* @param $answer
|
32 |
+
* @return static
|
33 |
+
*/
|
34 |
+
public static function make(Collector $collector, $pluginId, $path, $question, $answer)
|
35 |
+
{
|
36 |
+
return new static($collector, $pluginId, $path, $question, $answer);
|
37 |
+
}
|
38 |
+
|
39 |
+
public function __construct(Collector $collector, $pluginId, $path, $question, $answer)
|
40 |
+
{
|
41 |
+
parent::__construct($collector, $pluginId, $path);
|
42 |
+
|
43 |
+
$this->question = $question;
|
44 |
+
$this->answer = $answer;
|
45 |
+
}
|
46 |
+
|
47 |
+
public function toArray()
|
48 |
+
{
|
49 |
+
return array_merge(parent::toArray(), [
|
50 |
+
'question' => $this->question,
|
51 |
+
'answer' => $this->answer,
|
52 |
+
]);
|
53 |
+
}
|
54 |
+
|
55 |
+
/**
|
56 |
+
* Execute the request
|
57 |
+
* @param RequestorContract $requestor
|
58 |
+
* @return ApiResponse
|
59 |
+
*/
|
60 |
+
public function execute(RequestorContract $requestor)
|
61 |
+
{
|
62 |
+
return $requestor->post('logger/deactivate', $this->toArray());
|
63 |
+
}
|
64 |
+
}
|
analyst/src/Http/Requests/InstallRequest.php
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst\Http\Requests;
|
4 |
+
|
5 |
+
use Analyst\ApiResponse;
|
6 |
+
use Analyst\Collector;
|
7 |
+
use Analyst\Contracts\RequestorContract;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Class InstallRequest
|
11 |
+
*
|
12 |
+
* @since 0.9.4
|
13 |
+
*/
|
14 |
+
class InstallRequest extends AbstractLoggerRequest
|
15 |
+
{
|
16 |
+
/**
|
17 |
+
* Execute the request
|
18 |
+
* @param RequestorContract $requestor
|
19 |
+
* @return ApiResponse
|
20 |
+
*/
|
21 |
+
public function execute(RequestorContract $requestor)
|
22 |
+
{
|
23 |
+
return $requestor->post('logger/install', $this->toArray());
|
24 |
+
}
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Make request instance
|
28 |
+
*
|
29 |
+
* @param Collector $collector
|
30 |
+
* @param $pluginId
|
31 |
+
* @param $path
|
32 |
+
* @return static
|
33 |
+
*/
|
34 |
+
public static function make(Collector $collector, $pluginId, $path)
|
35 |
+
{
|
36 |
+
return new static($collector, $pluginId, $path);
|
37 |
+
}
|
38 |
+
}
|
analyst/src/Http/Requests/OptInRequest.php
ADDED
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst\Http\Requests;
|
4 |
+
|
5 |
+
use Analyst\ApiResponse;
|
6 |
+
use Analyst\Collector;
|
7 |
+
use Analyst\Contracts\RequestContract;
|
8 |
+
use Analyst\Contracts\RequestorContract;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Class OptInRequest
|
12 |
+
*
|
13 |
+
* Is is very similar to install request
|
14 |
+
* but with different path
|
15 |
+
*
|
16 |
+
* @since 0.9.5
|
17 |
+
*/
|
18 |
+
class OptInRequest extends AbstractLoggerRequest
|
19 |
+
{
|
20 |
+
/**
|
21 |
+
* Execute the request
|
22 |
+
* @param RequestorContract $requestor
|
23 |
+
* @return ApiResponse
|
24 |
+
*/
|
25 |
+
public function execute(RequestorContract $requestor)
|
26 |
+
{
|
27 |
+
return $requestor->post('logger/opt-in', $this->toArray());
|
28 |
+
}
|
29 |
+
|
30 |
+
/**
|
31 |
+
* Make request instance
|
32 |
+
*
|
33 |
+
* @param Collector $collector
|
34 |
+
* @param $pluginId
|
35 |
+
* @param $path
|
36 |
+
* @return static
|
37 |
+
*/
|
38 |
+
public static function make(Collector $collector, $pluginId, $path)
|
39 |
+
{
|
40 |
+
return new static($collector, $pluginId, $path);
|
41 |
+
}
|
42 |
+
}
|
analyst/src/Http/Requests/OptOutRequest.php
ADDED
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst\Http\Requests;
|
4 |
+
|
5 |
+
use Analyst\ApiResponse;
|
6 |
+
use Analyst\Collector;
|
7 |
+
use Analyst\Contracts\RequestContract;
|
8 |
+
use Analyst\Contracts\RequestorContract;
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Class OptOutRequest
|
12 |
+
*
|
13 |
+
* Is is very similar to install request
|
14 |
+
* but with different path
|
15 |
+
*
|
16 |
+
* @since 0.9.9
|
17 |
+
*/
|
18 |
+
class OptOutRequest extends AbstractLoggerRequest
|
19 |
+
{
|
20 |
+
/**
|
21 |
+
* @param Collector $collector
|
22 |
+
* @param $pluginId
|
23 |
+
* @param $path
|
24 |
+
* @return static
|
25 |
+
*/
|
26 |
+
public static function make(Collector $collector, $pluginId, $path)
|
27 |
+
{
|
28 |
+
return new static($collector, $pluginId, $path);
|
29 |
+
}
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Execute the request
|
33 |
+
* @param RequestorContract $requestor
|
34 |
+
* @return ApiResponse
|
35 |
+
*/
|
36 |
+
public function execute(RequestorContract $requestor)
|
37 |
+
{
|
38 |
+
return $requestor->post('logger/opt-out', $this->toArray());
|
39 |
+
}
|
40 |
+
}
|
analyst/src/Http/Requests/UninstallRequest.php
ADDED
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst\Http\Requests;
|
4 |
+
|
5 |
+
use Analyst\ApiResponse;
|
6 |
+
use Analyst\Collector;
|
7 |
+
use Analyst\Contracts\RequestorContract;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Class DeactivateRequest
|
11 |
+
*
|
12 |
+
* @since 0.9.13
|
13 |
+
*/
|
14 |
+
class UninstallRequest extends AbstractLoggerRequest
|
15 |
+
{
|
16 |
+
/**
|
17 |
+
* @param Collector $collector
|
18 |
+
* @param $pluginId
|
19 |
+
* @param $path
|
20 |
+
* @return static
|
21 |
+
*/
|
22 |
+
public static function make(Collector $collector, $pluginId, $path)
|
23 |
+
{
|
24 |
+
return new static($collector, $pluginId, $path);
|
25 |
+
}
|
26 |
+
|
27 |
+
/**
|
28 |
+
* Execute the request
|
29 |
+
* @param RequestorContract $requestor
|
30 |
+
* @return ApiResponse
|
31 |
+
*/
|
32 |
+
public function execute(RequestorContract $requestor)
|
33 |
+
{
|
34 |
+
return $requestor->post('logger/uninstall', $this->toArray());
|
35 |
+
}
|
36 |
+
}
|
analyst/src/Http/WordPressHttpClient.php
ADDED
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst\Http;
|
4 |
+
|
5 |
+
use WP_Error;
|
6 |
+
use Analyst\ApiResponse;
|
7 |
+
use Analyst\Contracts\HttpClientContract;
|
8 |
+
use Requests_Utility_CaseInsensitiveDictionary;
|
9 |
+
|
10 |
+
class WordPressHttpClient implements HttpClientContract
|
11 |
+
{
|
12 |
+
/**
|
13 |
+
* Make an http request
|
14 |
+
*
|
15 |
+
* @param $method
|
16 |
+
* @param $url
|
17 |
+
* @param $body
|
18 |
+
* @param $headers
|
19 |
+
* @return ApiResponse
|
20 |
+
*/
|
21 |
+
public function request($method, $url, $body, $headers)
|
22 |
+
{
|
23 |
+
$options = [
|
24 |
+
'body' => json_encode($body),
|
25 |
+
'headers' => $headers,
|
26 |
+
'method' => $method,
|
27 |
+
'timeout' => 30,
|
28 |
+
];
|
29 |
+
|
30 |
+
$response = wp_remote_request($url, $options);
|
31 |
+
|
32 |
+
$body = [];
|
33 |
+
$responseHeaders = [];
|
34 |
+
|
35 |
+
if ($response instanceof WP_Error) {
|
36 |
+
$code = $response->get_error_code();
|
37 |
+
} else {
|
38 |
+
/** @var Requests_Utility_CaseInsensitiveDictionary $headers */
|
39 |
+
$responseHeaders = $response['headers']->getAll();
|
40 |
+
$body = json_decode($response['body'], true);
|
41 |
+
$code = $response['response']['code'];
|
42 |
+
}
|
43 |
+
|
44 |
+
|
45 |
+
return new ApiResponse(
|
46 |
+
$body,
|
47 |
+
$code,
|
48 |
+
$responseHeaders
|
49 |
+
);
|
50 |
+
}
|
51 |
+
|
52 |
+
/**
|
53 |
+
* Must return `true` if client is supported
|
54 |
+
*
|
55 |
+
* @return bool
|
56 |
+
*/
|
57 |
+
public static function hasSupport()
|
58 |
+
{
|
59 |
+
return function_exists('wp_remote_request');
|
60 |
+
}
|
61 |
+
}
|
analyst/src/Mutator.php
ADDED
@@ -0,0 +1,103 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst;
|
4 |
+
|
5 |
+
use Analyst\Cache\DatabaseCache;
|
6 |
+
use Analyst\Contracts\CacheContract;
|
7 |
+
use Analyst\Notices\NoticeFactory;
|
8 |
+
|
9 |
+
/**
|
10 |
+
* Class Mutator mutates (modifies) UX with additional
|
11 |
+
* functional
|
12 |
+
*/
|
13 |
+
class Mutator
|
14 |
+
{
|
15 |
+
protected $notices = [];
|
16 |
+
|
17 |
+
/**
|
18 |
+
* @var NoticeFactory
|
19 |
+
*/
|
20 |
+
protected $factory;
|
21 |
+
|
22 |
+
/**
|
23 |
+
* @var CacheContract
|
24 |
+
*/
|
25 |
+
protected $cache;
|
26 |
+
|
27 |
+
public function __construct()
|
28 |
+
{
|
29 |
+
$this->factory = NoticeFactory::instance();
|
30 |
+
|
31 |
+
$this->notices = $this->factory->getNotices();
|
32 |
+
|
33 |
+
$this->cache = DatabaseCache::getInstance();
|
34 |
+
}
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Register filters all necessary stuff.
|
38 |
+
* Can be invoked only once.
|
39 |
+
*
|
40 |
+
* @return void
|
41 |
+
*/
|
42 |
+
public function initialize()
|
43 |
+
{
|
44 |
+
$this->registerLinks();
|
45 |
+
$this->registerAssets();
|
46 |
+
$this->registerHooks();
|
47 |
+
}
|
48 |
+
|
49 |
+
/**
|
50 |
+
* Register all necessary filters and templates
|
51 |
+
*
|
52 |
+
* @return void
|
53 |
+
*/
|
54 |
+
protected function registerLinks()
|
55 |
+
{
|
56 |
+
add_action('admin_footer', function () {
|
57 |
+
analyst_require_template('optout.php', [
|
58 |
+
'shieldImage' => analyst_assets_url('img/shield_question.png')
|
59 |
+
]);
|
60 |
+
|
61 |
+
analyst_require_template('optin.php');
|
62 |
+
|
63 |
+
analyst_require_template('forms/deactivate.php', [
|
64 |
+
'pencilImage' => analyst_assets_url('img/pencil.png'),
|
65 |
+
'smileImage' => analyst_assets_url('img/smile.png'),
|
66 |
+
]);
|
67 |
+
|
68 |
+
analyst_require_template('forms/install.php', [
|
69 |
+
'pluginToInstall' => $this->cache->get('plugin_to_install'),
|
70 |
+
'shieldImage' => analyst_assets_url('img/shield_success.png'),
|
71 |
+
]);
|
72 |
+
});
|
73 |
+
|
74 |
+
add_action('admin_notices',function () {
|
75 |
+
foreach ($this->notices as $notice) {
|
76 |
+
analyst_require_template('notice.php', ['notice' => $notice]);
|
77 |
+
}
|
78 |
+
});
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Register all assets
|
83 |
+
*/
|
84 |
+
public function registerAssets()
|
85 |
+
{
|
86 |
+
add_action('admin_enqueue_scripts', function () {
|
87 |
+
wp_enqueue_style('analyst_custom', analyst_assets_url('/css/customize.css'));
|
88 |
+
wp_enqueue_script('analyst_custom', analyst_assets_url('/js/customize.js'));
|
89 |
+
});
|
90 |
+
}
|
91 |
+
|
92 |
+
/**
|
93 |
+
* Register action hooks
|
94 |
+
*/
|
95 |
+
public function registerHooks()
|
96 |
+
{
|
97 |
+
add_action('wp_ajax_analyst_notification_dismiss', function () {
|
98 |
+
$this->factory->remove($_POST['id']);
|
99 |
+
|
100 |
+
$this->factory->sync();
|
101 |
+
});
|
102 |
+
}
|
103 |
+
}
|
analyst/src/Notices/Notice.php
ADDED
@@ -0,0 +1,121 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst\Notices;
|
4 |
+
|
5 |
+
class Notice
|
6 |
+
{
|
7 |
+
/**
|
8 |
+
* Id of notice
|
9 |
+
*
|
10 |
+
* @var string
|
11 |
+
*/
|
12 |
+
protected $id;
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Body of notice
|
16 |
+
*
|
17 |
+
* @var string
|
18 |
+
*/
|
19 |
+
protected $body;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Account id
|
23 |
+
*
|
24 |
+
* @var string
|
25 |
+
*/
|
26 |
+
protected $accountId;
|
27 |
+
|
28 |
+
/**
|
29 |
+
* The plugin name
|
30 |
+
*
|
31 |
+
* @var string
|
32 |
+
*/
|
33 |
+
protected $pluginName;
|
34 |
+
|
35 |
+
/**
|
36 |
+
* New notice
|
37 |
+
*
|
38 |
+
* @param $id
|
39 |
+
* @param $accountId
|
40 |
+
* @param $body
|
41 |
+
* @param null $pluginName
|
42 |
+
*
|
43 |
+
* @return Notice
|
44 |
+
*/
|
45 |
+
public static function make($id, $accountId, $body, $pluginName = null)
|
46 |
+
{
|
47 |
+
return new Notice($id, $accountId, $body, $pluginName);
|
48 |
+
}
|
49 |
+
|
50 |
+
public function __construct($id, $accountId, $body, $pluginName)
|
51 |
+
{
|
52 |
+
$this->setId($id);
|
53 |
+
$this->setBody($body);
|
54 |
+
$this->setAccountId($accountId);
|
55 |
+
$this->setPluginName($pluginName);
|
56 |
+
}
|
57 |
+
|
58 |
+
/**
|
59 |
+
* @return string
|
60 |
+
*/
|
61 |
+
public function getId()
|
62 |
+
{
|
63 |
+
return $this->id;
|
64 |
+
}
|
65 |
+
|
66 |
+
/**
|
67 |
+
* @param string $id
|
68 |
+
*/
|
69 |
+
public function setId($id)
|
70 |
+
{
|
71 |
+
$this->id = $id;
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* @return string
|
76 |
+
*/
|
77 |
+
public function getBody()
|
78 |
+
{
|
79 |
+
return $this->body;
|
80 |
+
}
|
81 |
+
|
82 |
+
/**
|
83 |
+
* @param string $body
|
84 |
+
*/
|
85 |
+
public function setBody($body)
|
86 |
+
{
|
87 |
+
$this->body = $body;
|
88 |
+
}
|
89 |
+
|
90 |
+
/**
|
91 |
+
* @return string
|
92 |
+
*/
|
93 |
+
public function getAccountId()
|
94 |
+
{
|
95 |
+
return $this->accountId;
|
96 |
+
}
|
97 |
+
|
98 |
+
/**
|
99 |
+
* @param string $accountId
|
100 |
+
*/
|
101 |
+
public function setAccountId($accountId)
|
102 |
+
{
|
103 |
+
$this->accountId = $accountId;
|
104 |
+
}
|
105 |
+
|
106 |
+
/**
|
107 |
+
* @return string|null
|
108 |
+
*/
|
109 |
+
public function getPluginName()
|
110 |
+
{
|
111 |
+
return $this->pluginName;
|
112 |
+
}
|
113 |
+
|
114 |
+
/**
|
115 |
+
* @param string $pluginName
|
116 |
+
*/
|
117 |
+
public function setPluginName($pluginName)
|
118 |
+
{
|
119 |
+
$this->pluginName = $pluginName;
|
120 |
+
}
|
121 |
+
}
|
analyst/src/Notices/NoticeFactory.php
ADDED
@@ -0,0 +1,130 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
namespace Analyst\Notices;
|
4 |
+
|
5 |
+
use Analyst\Core\AbstractFactory;
|
6 |
+
|
7 |
+
class NoticeFactory extends AbstractFactory
|
8 |
+
{
|
9 |
+
private static $instance;
|
10 |
+
|
11 |
+
CONST OPTIONS_KEY = 'analyst_notices';
|
12 |
+
|
13 |
+
/**
|
14 |
+
* Application notifications
|
15 |
+
*
|
16 |
+
* @var array
|
17 |
+
*/
|
18 |
+
protected $notices = [];
|
19 |
+
|
20 |
+
/**
|
21 |
+
* Read factory from options or make fresh instance
|
22 |
+
*
|
23 |
+
* @return NoticeFactory
|
24 |
+
*/
|
25 |
+
public static function instance()
|
26 |
+
{
|
27 |
+
if (!static::$instance) {
|
28 |
+
$raw = get_option(self::OPTIONS_KEY);
|
29 |
+
|
30 |
+
// In case object is already unserialized
|
31 |
+
// and instance of AccountDataFactory we
|
32 |
+
// return it, in other case deal with
|
33 |
+
// serialized string data
|
34 |
+
if ($raw instanceof self) {
|
35 |
+
static::$instance = $raw;
|
36 |
+
} else {
|
37 |
+
static::$instance = is_string($raw) ? static::unserialize($raw) : new self();
|
38 |
+
}
|
39 |
+
}
|
40 |
+
|
41 |
+
return static::$instance;
|
42 |
+
}
|
43 |
+
|
44 |
+
/**
|
45 |
+
* Sync this object data with cache
|
46 |
+
*/
|
47 |
+
public function sync()
|
48 |
+
{
|
49 |
+
update_option(self::OPTIONS_KEY, serialize($this));
|
50 |
+
}
|
51 |
+
|
52 |
+
/**
|
53 |
+
* Sync this instance data with cache
|
54 |
+
*/
|
55 |
+
public static function syncData()
|
56 |
+
{
|
57 |
+
static::instance()->sync();
|
58 |
+
}
|
59 |
+
|
60 |
+
/**
|
61 |
+
* @return array
|
62 |
+
*/
|
63 |
+
public function getNotices()
|
64 |
+
{
|
65 |
+
return $this->notices;
|
66 |
+
}
|
67 |
+
|
68 |
+
/**
|
69 |
+
* Filter out notices for certain account
|
70 |
+
*
|
71 |
+
* @param $accountId
|
72 |
+
* @return array
|
73 |
+
*/
|
74 |
+
public function getNoticesForAccount($accountId)
|
75 |
+
{
|
76 |
+
return array_filter($this->notices, function (Notice $notice) use ($accountId) {
|
77 |
+
return $notice->getAccountId() === $accountId;
|
78 |
+
});
|
79 |
+
}
|
80 |
+
|
81 |
+
/**
|
82 |
+
* Add new notice
|
83 |
+
*
|
84 |
+
* @param $notice
|
85 |
+
*
|
86 |
+
* @return $this
|
87 |
+
*/
|
88 |
+
public function addNotice($notice)
|
89 |
+
{
|
90 |
+
array_push($this->notices, $notice);
|
91 |
+
|
92 |
+
$this->sync();
|
93 |
+
|
94 |
+
return $this;
|
95 |
+
}
|
96 |
+
|
97 |
+
/**
|
98 |
+
* Find notice by id
|
99 |
+
*
|
100 |
+
* @param $id
|
101 |
+
* @return Notice|null
|
102 |
+
*/
|
103 |
+
public function find($id)
|
104 |
+
{
|
105 |
+
$notices = array_filter($this->notices, function (Notice $notice) use ($id) {
|
106 |
+
return $notice->getId() === $id;
|
107 |
+
});
|
108 |
+
|
109 |
+
return array_pop($notices);
|
110 |
+
}
|
111 |
+
|
112 |
+
/**
|
113 |
+
* Remove notice by it's id
|
114 |
+
*
|
115 |
+
* @param $id
|
116 |
+
*/
|
117 |
+
public function remove($id)
|
118 |
+
{
|
119 |
+
// Get key of notice to remove
|
120 |
+
$key = array_search(
|
121 |
+
$this->find($id),
|
122 |
+
$this->notices
|
123 |
+
);
|
124 |
+
|
125 |
+
// Unset notice with key
|
126 |
+
unset($this->notices[$key]);
|
127 |
+
|
128 |
+
$this->sync();
|
129 |
+
}
|
130 |
+
}
|
analyst/src/helpers.php
ADDED
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
if (! function_exists('analyst_assets_path')) {
|
4 |
+
/**
|
5 |
+
* Generates path to file in assets folder
|
6 |
+
*
|
7 |
+
* @param $file
|
8 |
+
* @return string
|
9 |
+
*/
|
10 |
+
function analyst_assets_path($file)
|
11 |
+
{
|
12 |
+
$path = sprintf('%s/assets/%s', realpath(__DIR__ . '/..'), trim($file, '/'));
|
13 |
+
|
14 |
+
return wp_normalize_path($path);
|
15 |
+
}
|
16 |
+
}
|
17 |
+
|
18 |
+
|
19 |
+
if (! function_exists('analyst_assets_url')) {
|
20 |
+
/**
|
21 |
+
* Generates url to file in assets folder
|
22 |
+
*
|
23 |
+
* @param $file
|
24 |
+
* @return string
|
25 |
+
*/
|
26 |
+
function analyst_assets_url($file)
|
27 |
+
{
|
28 |
+
$absolutePath = analyst_assets_path($file);
|
29 |
+
|
30 |
+
// We can always rely on WP_PLUGIN_DIR, because that's where
|
31 |
+
// wordpress install it's plugin's. So we remove last segment
|
32 |
+
// of that path to get the content dir AKA directly where
|
33 |
+
// plugins are installed and make the magic...
|
34 |
+
$contentDir = is_link(WP_PLUGIN_DIR) ?
|
35 |
+
dirname(wp_normalize_path(readlink(WP_PLUGIN_DIR))) :
|
36 |
+
dirname(wp_normalize_path(WP_PLUGIN_DIR));
|
37 |
+
|
38 |
+
$relativePath = str_replace( $contentDir, '', $absolutePath);
|
39 |
+
|
40 |
+
return content_url(wp_normalize_path($relativePath));
|
41 |
+
}
|
42 |
+
}
|
43 |
+
|
44 |
+
if (! function_exists('analyst_templates_path')) {
|
45 |
+
/**
|
46 |
+
* Generates path to file in templates folder
|
47 |
+
*
|
48 |
+
* @param $file
|
49 |
+
* @return string
|
50 |
+
*/
|
51 |
+
function analyst_templates_path($file)
|
52 |
+
{
|
53 |
+
$path = sprintf('%s/templates/%s', realpath(__DIR__ . '/..'), trim($file, '/'));
|
54 |
+
|
55 |
+
return wp_normalize_path($path);
|
56 |
+
}
|
57 |
+
}
|
58 |
+
|
59 |
+
if (! function_exists('analyst_require_template')) {
|
60 |
+
/**
|
61 |
+
* Require certain template with data
|
62 |
+
*
|
63 |
+
* @param $file
|
64 |
+
* @param array $data
|
65 |
+
*/
|
66 |
+
function analyst_require_template($file, $data = [])
|
67 |
+
{
|
68 |
+
// Extract data to current scope table
|
69 |
+
extract($data);
|
70 |
+
|
71 |
+
require analyst_templates_path($file);
|
72 |
+
}
|
73 |
+
}
|
74 |
+
|
75 |
+
if (! function_exists('dd')) {
|
76 |
+
/**
|
77 |
+
* Dump some data
|
78 |
+
*/
|
79 |
+
function dd ()
|
80 |
+
{
|
81 |
+
var_dump(func_get_args());
|
82 |
+
die();
|
83 |
+
}
|
84 |
+
}
|
analyst/templates/forms/deactivate.php
ADDED
@@ -0,0 +1,156 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<div id="analyst-deactivate-modal" class="analyst-modal" style="display: none">
|
2 |
+
<div class="analyst-modal-content" style="width: 500px">
|
3 |
+
<div class="analyst-disable-modal-mask" id="analyst-disable-deactivate-modal-mask" style="display: none"></div>
|
4 |
+
<div style="display: flex">
|
5 |
+
<div class="analyst-install-image-block" style="width: 80px">
|
6 |
+
<img src="<?=$pencilImage?>"/>
|
7 |
+
</div>
|
8 |
+
<div class="analyst-install-description-block" style="padding-left: 20px">
|
9 |
+
<strong class="analyst-modal-header">Why do you deactivate?</strong>
|
10 |
+
<div class="analyst-install-description-text" style="padding-top: 2px">
|
11 |
+
Please let us know, so we can improve it! Thank you <img class="analyst-smile-image" src="<?=$smileImage?>" alt="">
|
12 |
+
</div>
|
13 |
+
</div>
|
14 |
+
</div>
|
15 |
+
<div>
|
16 |
+
<ul id="analyst-deactivation-reasons">
|
17 |
+
<li>
|
18 |
+
<label>
|
19 |
+
<span>
|
20 |
+
<input type="radio" name="deactivation-reason">
|
21 |
+
</span>
|
22 |
+
<span class="question" data-question="I couldn't understand how to make it work">I couldn't understand how to make it work</span>
|
23 |
+
</label>
|
24 |
+
</li>
|
25 |
+
<li data-input-type="textarea" data-input-placeholder="What should have worked, but didn’t?">
|
26 |
+
<label>
|
27 |
+
<span>
|
28 |
+
<input type="radio" name="deactivation-reason">
|
29 |
+
</span>
|
30 |
+
<span class="question" data-question="The plugin didn't work as expected">The plugin didn't work as expected</span>
|
31 |
+
</label>
|
32 |
+
<div class="question-answer"></div>
|
33 |
+
</li>
|
34 |
+
<li data-input-type="input" data-input-placeholder="What is the plugin name?">
|
35 |
+
<label>
|
36 |
+
<span>
|
37 |
+
<input type="radio" name="deactivation-reason">
|
38 |
+
</span>
|
39 |
+
<span class="question" data-question="I found a better plugin">I found a better plugin</span>
|
40 |
+
</label>
|
41 |
+
<div class="question-answer"></div>
|
42 |
+
</li>
|
43 |
+
<li>
|
44 |
+
<label>
|
45 |
+
<span>
|
46 |
+
<input type="radio" name="deactivation-reason">
|
47 |
+
</span>
|
48 |
+
<span class="question" data-question="It's a temporary deactivation">It's a temporary deactivation</span>
|
49 |
+
</label>
|
50 |
+
<div class="question-answer"></div>
|
51 |
+
</li>
|
52 |
+
<li data-input-type="textarea" data-input-placeholder="Please provide the reason of deactivation">
|
53 |
+
<label>
|
54 |
+
<span>
|
55 |
+
<input type="radio" name="deactivation-reason">
|
56 |
+
</span>
|
57 |
+
<span class="question" data-question="Other">Other</span>
|
58 |
+
</label>
|
59 |
+
<div class="question-answer"></div>
|
60 |
+
</li>
|
61 |
+
</ul>
|
62 |
+
<p id="analyst-deactivation-error" style="color: #dc3232; font-size: 16px; display: none">Please let us know the reason for de-activation. Thank you!</p>
|
63 |
+
</div>
|
64 |
+
<div>
|
65 |
+
<button class="analyst-btn-grey" id="analyst-disabled-plugin-action">Deactivate</button>
|
66 |
+
</div>
|
67 |
+
<div class="" style="text-align: center; font-size: 18px; padding-top: 10px">
|
68 |
+
<button class="analyst-btn-secondary-ghost analyst-deactivate-modal-close" style="color: #cccccc">Cancel</button>
|
69 |
+
</div>
|
70 |
+
</div>
|
71 |
+
</div>
|
72 |
+
|
73 |
+
<script type="text/javascript">
|
74 |
+
(function ($) {
|
75 |
+
$('.deactivate').click(function (e) {
|
76 |
+
var anchor = $(this).find('[analyst-plugin-id]')
|
77 |
+
var pluginId = anchor.attr('analyst-plugin-id')
|
78 |
+
var isOptedIn = anchor.attr('analyst-plugin-opted-in') === '1'
|
79 |
+
|
80 |
+
// Do not ask for reason if not opted in
|
81 |
+
if (!isOptedIn) {
|
82 |
+
return
|
83 |
+
}
|
84 |
+
|
85 |
+
e.preventDefault()
|
86 |
+
|
87 |
+
$('#analyst-deactivate-modal')
|
88 |
+
.attr({
|
89 |
+
'analyst-plugin-id': pluginId,
|
90 |
+
'analyst-redirect-url': $(this).find('a').attr('href')
|
91 |
+
})
|
92 |
+
.show()
|
93 |
+
})
|
94 |
+
|
95 |
+
$('.analyst-deactivate-modal-close').click(function () {
|
96 |
+
$('#analyst-deactivate-modal').hide()
|
97 |
+
})
|
98 |
+
|
99 |
+
$('#analyst-deactivation-reasons input[name="deactivation-reason"]').change(function () {
|
100 |
+
$('.question-answer').empty()
|
101 |
+
|
102 |
+
var root = $('#analyst-deactivation-reasons input[name="deactivation-reason"]:checked').parents('li')
|
103 |
+
|
104 |
+
$('#analyst-deactivation-error').hide()
|
105 |
+
|
106 |
+
if (!root.attr('data-input-type')) return
|
107 |
+
|
108 |
+
var reasonInput = $('<' + root.attr('data-input-type') + '/>').attr({placeholder: root.attr('data-input-placeholder'), class: 'reason-answer'})
|
109 |
+
|
110 |
+
root.find('.question-answer').append(reasonInput)
|
111 |
+
})
|
112 |
+
|
113 |
+
$('#analyst-disabled-plugin-action').click(function () {
|
114 |
+
var pluginId = $('#analyst-deactivate-modal').attr('analyst-plugin-id')
|
115 |
+
var pluginDeactivationUrl = $('#analyst-deactivate-modal').attr('analyst-redirect-url')
|
116 |
+
|
117 |
+
var root = $('#analyst-deactivation-reasons input[name="deactivation-reason"]:checked').parents('li');
|
118 |
+
|
119 |
+
var reason = root.find('.question-answer .reason-answer').val();
|
120 |
+
|
121 |
+
var question = root.find('.question').attr('data-question').trim()
|
122 |
+
|
123 |
+
var $errorBlock = $('#analyst-deactivation-error')
|
124 |
+
|
125 |
+
if (!question) {
|
126 |
+
return $errorBlock.show()
|
127 |
+
}
|
128 |
+
|
129 |
+
$errorBlock.hide()
|
130 |
+
|
131 |
+
var data = {
|
132 |
+
action: 'analyst_plugin_deactivate_' + pluginId,
|
133 |
+
question: question
|
134 |
+
}
|
135 |
+
|
136 |
+
if (reason) {
|
137 |
+
data['reason'] = reason.trim();
|
138 |
+
}
|
139 |
+
|
140 |
+
$(this).attr('disabled', true).text('Deactivating...');
|
141 |
+
|
142 |
+
$('#analyst-disable-deactivate-modal-mask').show();
|
143 |
+
|
144 |
+
$.ajax({
|
145 |
+
url: ajaxurl,
|
146 |
+
method: 'POST',
|
147 |
+
data: data
|
148 |
+
}).done(function () {
|
149 |
+
window.location.href = pluginDeactivationUrl
|
150 |
+
|
151 |
+
$('#analyst-disable-deactivate-modal-mask').hide();
|
152 |
+
})
|
153 |
+
})
|
154 |
+
|
155 |
+
})(jQuery)
|
156 |
+
</script>
|
analyst/templates/forms/install.php
ADDED
@@ -0,0 +1,113 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<div id="analyst-install-modal" class="analyst-modal" style="display: none" analyst-plugin-id="<?=$pluginToInstall?>">
|
2 |
+
<div class="analyst-modal-content" style="width: 450px">
|
3 |
+
<div class="analyst-disable-modal-mask" id="analyst-disable-install-modal-mask" style="display: none"></div>
|
4 |
+
<div style="display: flex">
|
5 |
+
<div class="analyst-install-image-block">
|
6 |
+
<img src="<?=$shieldImage?>"/>
|
7 |
+
</div>
|
8 |
+
<div class="analyst-install-description-block">
|
9 |
+
<strong class="analyst-modal-header">Stay on the safe side</strong>
|
10 |
+
<p class="analyst-install-description-text">Receive our plugin’s alerts in
|
11 |
+
case of <strong>critical security</strong> & feature
|
12 |
+
updates and allow non-sensitive
|
13 |
+
diagnostic tracking.</p>
|
14 |
+
</div>
|
15 |
+
</div>
|
16 |
+
<div class="analyst-modal-def-top-padding">
|
17 |
+
<button class="analyst-btn-success" id="analyst-install-action">Allow & Continue ></button>
|
18 |
+
</div>
|
19 |
+
<div class="analyst-modal-def-top-padding" id="analyst-permissions-block" style="display: none">
|
20 |
+
<span>You’re granting these permissions:</span>
|
21 |
+
<ul class="analyst-install-permissions-list">
|
22 |
+
<li><strong>Your profile information</strong> (name and email) </li>
|
23 |
+
<li><strong>Your site information</strong> (URL, WP version, PHP info, plugins & themes)</li>
|
24 |
+
<li><strong>Plugin notices</strong> (updates, announcements, marketing, no spam)</li>
|
25 |
+
<li><strong>Plugin events</strong> (activation, deactivation and uninstall)</li>
|
26 |
+
</ul>
|
27 |
+
</div>
|
28 |
+
<div class="analyst-install-footer analyst-modal-def-top-padding">
|
29 |
+
<span class="analyst-action-text" id="analyst-permissions-toggle">Learn more</span>
|
30 |
+
<span id="analyst-powered-by" style="display: none;">Powered by <a href="https://sellcodes.com/blog/wordpress-feedback-system-for-plugin-creators/?utm_source=optin_screen" target="_blank" class="analyst-link">Sellcodes.com</a></span>
|
31 |
+
<span class="analyst-action-text analyst-install-modal-close" id="analyst-install-skip">Skip</span>
|
32 |
+
</div>
|
33 |
+
<div id="analyst-install-error" class="analyst-modal-def-top-padding" style="display: none; text-align: center">
|
34 |
+
<span style="color: #dc3232; font-size: 16px">Service unavailable. Please try again later</span>
|
35 |
+
</div>
|
36 |
+
</div>
|
37 |
+
</div>
|
38 |
+
|
39 |
+
<script type="text/javascript">
|
40 |
+
(function ($) {
|
41 |
+
|
42 |
+
var installPlugin = function (pluginId) {
|
43 |
+
var $error = $('#analyst-install-error')
|
44 |
+
|
45 |
+
$error.hide()
|
46 |
+
|
47 |
+
$.ajax({
|
48 |
+
url: ajaxurl,
|
49 |
+
method: 'POST',
|
50 |
+
data: {
|
51 |
+
action: 'analyst_install_' + pluginId
|
52 |
+
},
|
53 |
+
success: function (data) {
|
54 |
+
if (data && !data.success) {
|
55 |
+
//error
|
56 |
+
$('#analyst-install-modal').hide()
|
57 |
+
|
58 |
+
return
|
59 |
+
}
|
60 |
+
|
61 |
+
window.location.reload()
|
62 |
+
},
|
63 |
+
error: function () {
|
64 |
+
$('#analyst-install-modal').hide()
|
65 |
+
}
|
66 |
+
}).done(function () {
|
67 |
+
$('#analyst-disable-install-modal-mask').hide()
|
68 |
+
|
69 |
+
$('#analyst-install-action')
|
70 |
+
.attr('disabled', false)
|
71 |
+
.text('Allow & Continue >')
|
72 |
+
})
|
73 |
+
}
|
74 |
+
|
75 |
+
if ($('#analyst-install-modal').attr('analyst-plugin-id')) {
|
76 |
+
$('#analyst-install-modal').show()
|
77 |
+
}
|
78 |
+
|
79 |
+
|
80 |
+
$('.analyst-install-modal-close').click(function () {
|
81 |
+
$('#analyst-install-modal').hide()
|
82 |
+
})
|
83 |
+
|
84 |
+
$('#analyst-install-action').click(function () {
|
85 |
+
var pluginId = $('#analyst-install-modal').attr('analyst-plugin-id')
|
86 |
+
|
87 |
+
$('#analyst-install-action')
|
88 |
+
.attr('disabled', true)
|
89 |
+
.text('Please wait...')
|
90 |
+
|
91 |
+
$('#analyst-disable-install-modal-mask').show()
|
92 |
+
|
93 |
+
installPlugin(pluginId)
|
94 |
+
})
|
95 |
+
|
96 |
+
$('#analyst-permissions-toggle').click(function () {
|
97 |
+
var isVisible = $('#analyst-permissions-block').toggle().is(':visible')
|
98 |
+
|
99 |
+
isVisible ? $(this).text('Close section') : $(this).text('Learn more')
|
100 |
+
|
101 |
+
var poweredBy = $('#analyst-powered-by')
|
102 |
+
isVisible ? poweredBy.show() : poweredBy.hide()
|
103 |
+
})
|
104 |
+
|
105 |
+
$('#analyst-install-skip').click(function () {
|
106 |
+
var pluginId = $('#analyst-install-modal').attr('analyst-plugin-id')
|
107 |
+
|
108 |
+
$.post(ajaxurl, {action: 'analyst_skip_install_' + pluginId}).done(function () {
|
109 |
+
$('#analyst-install-modal').hide()
|
110 |
+
})
|
111 |
+
})
|
112 |
+
})(jQuery)
|
113 |
+
</script>
|
analyst/templates/notice.php
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<div class="notice notice-success analyst-notice">
|
2 |
+
<p>
|
3 |
+
<strong class="analyst-plugin-name"><?=$notice->getPluginName()?></strong>
|
4 |
+
<?=$notice->getBody()?>
|
5 |
+
</p>
|
6 |
+
|
7 |
+
<button type="button" class="analyst-notice-dismiss notice-dismiss" analyst-notice-id="<?=$notice->getId()?>">
|
8 |
+
<span class="screen-reader-text">Dismiss this notice.</span>
|
9 |
+
</button>
|
10 |
+
</div>
|
analyst/templates/optin.php
ADDED
@@ -0,0 +1,60 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script type="text/javascript">
|
2 |
+
|
3 |
+
(function ($) {
|
4 |
+
var isOptingIn = false
|
5 |
+
|
6 |
+
$('#analyst-opt-in-modal').appendTo($('body'))
|
7 |
+
|
8 |
+
var makeOptIn = function (pluginId) {
|
9 |
+
if (isOptingIn) return
|
10 |
+
|
11 |
+
isOptingIn = true
|
12 |
+
|
13 |
+
$.ajax({
|
14 |
+
url: ajaxurl,
|
15 |
+
method: 'POST',
|
16 |
+
data: {
|
17 |
+
action: 'analyst_opt_in_' + pluginId,
|
18 |
+
},
|
19 |
+
success: function () {
|
20 |
+
$('#analyst-opt-in-modal').hide()
|
21 |
+
|
22 |
+
isOptingIn = false
|
23 |
+
|
24 |
+
var optOutAction = $('<a />').attr({
|
25 |
+
class: 'analyst-action-opt analyst-opt-out',
|
26 |
+
'analyst-plugin-id': pluginId,
|
27 |
+
'analyst-plugin-signed': '1'
|
28 |
+
})
|
29 |
+
.text('Opt Out')
|
30 |
+
$('.analyst-opt-in[analyst-plugin-id="'+ pluginId +'"').replaceWith(optOutAction)
|
31 |
+
|
32 |
+
$('[analyst-plugin-id="' + pluginId + '"').attr('analyst-plugin-opted-in', 1)
|
33 |
+
}
|
34 |
+
})
|
35 |
+
}
|
36 |
+
|
37 |
+
$(document).on('click', '.analyst-opt-in:not([loading])', function() {
|
38 |
+
var pluginId = $(this).attr('analyst-plugin-id')
|
39 |
+
var isSigned = $(this).attr('analyst-plugin-signed') === '1'
|
40 |
+
|
41 |
+
if (!isSigned) {
|
42 |
+
$('#analyst-install-modal')
|
43 |
+
.attr('analyst-plugin-id', pluginId)
|
44 |
+
.show()
|
45 |
+
|
46 |
+
return;
|
47 |
+
}
|
48 |
+
|
49 |
+
$('#analyst-install-modal').attr({'analyst-plugin-id': pluginId})
|
50 |
+
|
51 |
+
$(this).attr('loading', true).text('Opting In...')
|
52 |
+
|
53 |
+
makeOptIn(pluginId);
|
54 |
+
})
|
55 |
+
|
56 |
+
$('.opt-in-modal-close').click(function () {
|
57 |
+
$('#analyst-opt-in-modal').hide()
|
58 |
+
})
|
59 |
+
})(jQuery)
|
60 |
+
</script>
|
analyst/templates/optout.php
ADDED
@@ -0,0 +1,109 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<div id="analyst-opt-out-modal" class="analyst-modal" style="display: none">
|
2 |
+
<div class="analyst-modal-content" style="width: 600px">
|
3 |
+
<div class="analyst-disable-modal-mask" id="analyst-disable-opt-out-modal-mask" style="display: none"></div>
|
4 |
+
<div style="display: flex">
|
5 |
+
<div class="analyst-install-image-block" style="width: 120px">
|
6 |
+
<img src="<?=$shieldImage?>"/>
|
7 |
+
</div>
|
8 |
+
<div class="analyst-install-description-block">
|
9 |
+
<strong class="analyst-modal-header">By opting out, we cannot alert you anymore in case of important security updates.</strong>
|
10 |
+
<p class="analyst-install-description-text">
|
11 |
+
In addition, we won’t get pointers how to further improve the plugin based on your integration with our plugin.
|
12 |
+
</p>
|
13 |
+
</div>
|
14 |
+
</div>
|
15 |
+
<div class="analyst-modal-def-top-padding">
|
16 |
+
<button class="analyst-btn-success opt-out-modal-close">Ok, don't opt out</button>
|
17 |
+
</div>
|
18 |
+
<div class="analyst-modal-def-top-padding" style="text-align: center;">
|
19 |
+
<button class="analyst-btn-secondary-ghost" id="opt-out-action">Opt out</button>
|
20 |
+
</div>
|
21 |
+
<div id="analyst-opt-out-error" class="analyst-modal-def-top-padding" style="display: none;">
|
22 |
+
<span style="color: #dc3232; font-size: 16px">Service unavailable. Please try again later</span>
|
23 |
+
</div>
|
24 |
+
</div>
|
25 |
+
</div>
|
26 |
+
</div>
|
27 |
+
|
28 |
+
<script type="text/javascript">
|
29 |
+
|
30 |
+
(function ($) {
|
31 |
+
var isOptingOut = false
|
32 |
+
|
33 |
+
$('#analyst-opt-out-modal').appendTo($('body'))
|
34 |
+
|
35 |
+
$(document).on('click', '.analyst-opt-out', function() {
|
36 |
+
var pluginId = $(this).attr('analyst-plugin-id')
|
37 |
+
|
38 |
+
$('#analyst-opt-out-modal')
|
39 |
+
.attr({'analyst-plugin-id': pluginId})
|
40 |
+
.show()
|
41 |
+
})
|
42 |
+
|
43 |
+
$('.opt-out-modal-close').click(function () {
|
44 |
+
$('#analyst-opt-out-modal').hide()
|
45 |
+
})
|
46 |
+
|
47 |
+
$('#opt-out-action').click(function () {
|
48 |
+
if (isOptingOut) return
|
49 |
+
|
50 |
+
var $mask = $('#analyst-disable-opt-out-modal-mask')
|
51 |
+
var $error = $('#analyst-opt-out-error')
|
52 |
+
|
53 |
+
var pluginId = $('#analyst-opt-out-modal').attr('analyst-plugin-id')
|
54 |
+
|
55 |
+
$mask.show()
|
56 |
+
$error.hide()
|
57 |
+
|
58 |
+
var self = this
|
59 |
+
|
60 |
+
isOptingOut = true
|
61 |
+
|
62 |
+
$(self).text('Opting out...')
|
63 |
+
|
64 |
+
$.ajax({
|
65 |
+
url: ajaxurl,
|
66 |
+
method: 'POST',
|
67 |
+
data: {
|
68 |
+
action: 'analyst_opt_out_' + pluginId,
|
69 |
+
},
|
70 |
+
success: function (data) {
|
71 |
+
$(self).text('Opt out')
|
72 |
+
|
73 |
+
if (data && !data.success) {
|
74 |
+
$('#analyst-opt-out-modal').hide()
|
75 |
+
|
76 |
+
return
|
77 |
+
}
|
78 |
+
|
79 |
+
$error.hide()
|
80 |
+
|
81 |
+
$('#analyst-opt-out-modal').hide()
|
82 |
+
|
83 |
+
isOptingOut = false
|
84 |
+
|
85 |
+
var optInAction = $('<a />').attr({
|
86 |
+
class: 'analyst-action-opt analyst-opt-in',
|
87 |
+
'analyst-plugin-id': pluginId,
|
88 |
+
'analyst-plugin-signed': '1'
|
89 |
+
})
|
90 |
+
.text('Opt In')
|
91 |
+
$('.analyst-opt-out[analyst-plugin-id="'+ pluginId +'"').replaceWith(optInAction)
|
92 |
+
|
93 |
+
$('[analyst-plugin-id="' + pluginId + '"').attr('analyst-plugin-opted-in', 0)
|
94 |
+
|
95 |
+
$mask.hide()
|
96 |
+
},
|
97 |
+
error: function () {
|
98 |
+
$('#analyst-opt-out-error').show()
|
99 |
+
|
100 |
+
$(self).text('Opt out')
|
101 |
+
}
|
102 |
+
}).done(function () {
|
103 |
+
$mask.hide()
|
104 |
+
|
105 |
+
isOptingOut = false
|
106 |
+
})
|
107 |
+
})
|
108 |
+
})(jQuery)
|
109 |
+
</script>
|
analyst/version.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
return array(
|
4 |
+
// The sdk version
|
5 |
+
'sdk' => '1.3.30',
|
6 |
+
|
7 |
+
// Minimum supported WordPress version
|
8 |
+
'wp' => '4.7',
|
9 |
+
|
10 |
+
// Supported PHP version
|
11 |
+
'php' => '5.4',
|
12 |
+
|
13 |
+
// Path to current SDK$
|
14 |
+
'path' => __DIR__,
|
15 |
+
);
|
enhanced-text-widget.php
CHANGED
@@ -3,13 +3,21 @@
|
|
3 |
Plugin Name: Enhanced Text Widget
|
4 |
Plugin URI: http://wordpress.org/plugins/enhanced-text-widget/
|
5 |
Description: An enhanced version of the default text widget where you may have Text, HTML, CSS, JavaScript, Flash, Shortcodes, and/or PHP as content with linkable widget title.
|
6 |
-
Version: 1.4.
|
7 |
-
Author:
|
8 |
-
Author URI:
|
9 |
Text Domain: enhancedtext
|
10 |
Domain Path: /languages/
|
11 |
License: MIT
|
12 |
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
13 |
|
14 |
class EnhancedTextWidget extends WP_Widget {
|
15 |
|
3 |
Plugin Name: Enhanced Text Widget
|
4 |
Plugin URI: http://wordpress.org/plugins/enhanced-text-widget/
|
5 |
Description: An enhanced version of the default text widget where you may have Text, HTML, CSS, JavaScript, Flash, Shortcodes, and/or PHP as content with linkable widget title.
|
6 |
+
Version: 1.4.7
|
7 |
+
Author: Clever Widgets
|
8 |
+
Author URI: https://themecheck.info
|
9 |
Text Domain: enhancedtext
|
10 |
Domain Path: /languages/
|
11 |
License: MIT
|
12 |
*/
|
13 |
+
require_once 'analyst/main.php';
|
14 |
+
|
15 |
+
analyst_init(array(
|
16 |
+
'client-id' => 'ozb8r4zvwq369anp',
|
17 |
+
'client-secret' => '63fcba7fd64e28937637a50f84450df531eb2dae',
|
18 |
+
'base-dir' => __FILE__
|
19 |
+
));
|
20 |
+
|
21 |
|
22 |
class EnhancedTextWidget extends WP_Widget {
|
23 |
|
readme.txt
CHANGED
@@ -1,10 +1,10 @@
|
|
1 |
=== Enhanced Text Widget ===
|
2 |
-
Contributors:
|
3 |
-
Donate link:
|
4 |
Tags: widget, clickable, linkable, linked title, text, php, javascript, flash, linked title text, linked, text widget, php widget, link widget title, bare widget, widget shortcodes, enhanced text, better text widget, simple, html widget, css
|
5 |
Requires at least: 3.6
|
6 |
-
Tested up to:
|
7 |
-
Stable tag: 1.4.
|
8 |
License: MIT
|
9 |
License URI: http://opensource.org/licenses/MIT
|
10 |
|
@@ -12,6 +12,8 @@ An enhanced version of the text widget that supports Text, HTML, CSS, JavaScript
|
|
12 |
|
13 |
== Description ==
|
14 |
|
|
|
|
|
15 |
An enhanced version of the default text widget where you may have Text, HTML, CSS, JavaScript, Flash, Shortcodes and/or PHP as content with linkable widget title.
|
16 |
|
17 |
= Options =
|
@@ -25,18 +27,12 @@ An enhanced version of the default text widget where you may have Text, HTML, CS
|
|
25 |
* Option to automatically add paragraphs to content
|
26 |
* Option to not output before/after_widget/title (bare widget)
|
27 |
|
28 |
-
= More Information =
|
29 |
|
30 |
-
|
31 |
-
* Fork or contribute on [Github](https://github.com/bostondv/enhanced-text-widget/)
|
32 |
-
* Follow me on [Twitter](http://twitter.com/bostondv/)
|
33 |
-
* View my other [WordPress Plugins](http://profiles.wordpress.org/bostondv/)
|
34 |
-
|
35 |
-
Made with <3 by [Boston Dell-Vandenberg](https://bostondv.com/)
|
36 |
|
37 |
= Support =
|
38 |
|
39 |
-
Enjoy this plugin? [Send a tip to support development](
|
40 |
|
41 |
== Installation ==
|
42 |
|
@@ -68,6 +64,11 @@ This adds a CSS class parameter to each widget.
|
|
68 |
This is the initial release.
|
69 |
|
70 |
== Changelog ==
|
|
|
|
|
|
|
|
|
|
|
71 |
|
72 |
= 1.4.6 =
|
73 |
* Tested up to WordPress 4.8
|
1 |
=== Enhanced Text Widget ===
|
2 |
+
Contributors: cl272
|
3 |
+
Donate link: https://sellcodes.com/5U4SICyc
|
4 |
Tags: widget, clickable, linkable, linked title, text, php, javascript, flash, linked title text, linked, text widget, php widget, link widget title, bare widget, widget shortcodes, enhanced text, better text widget, simple, html widget, css
|
5 |
Requires at least: 3.6
|
6 |
+
Tested up to: 5.3
|
7 |
+
Stable tag: 1.4.7
|
8 |
License: MIT
|
9 |
License URI: http://opensource.org/licenses/MIT
|
10 |
|
12 |
|
13 |
== Description ==
|
14 |
|
15 |
+
UPDATE: Plugin ownership changed for this plugin. We are currently evaluating possible enhancements for it. Stay tuned! If you have any suggestions yourself, please let us know in the Support Forum.
|
16 |
+
|
17 |
An enhanced version of the default text widget where you may have Text, HTML, CSS, JavaScript, Flash, Shortcodes and/or PHP as content with linkable widget title.
|
18 |
|
19 |
= Options =
|
27 |
* Option to automatically add paragraphs to content
|
28 |
* Option to not output before/after_widget/title (bare widget)
|
29 |
|
|
|
30 |
|
31 |
+
For questions, please ask in the [support forum](http://wordpress.org/support/plugin/enhanced-text-widget/)
|
|
|
|
|
|
|
|
|
|
|
32 |
|
33 |
= Support =
|
34 |
|
35 |
+
Enjoy this plugin? [Send a tip to support development](https://sellcodes.com/5U4SICyc).
|
36 |
|
37 |
== Installation ==
|
38 |
|
64 |
This is the initial release.
|
65 |
|
66 |
== Changelog ==
|
67 |
+
|
68 |
+
= 1.4.7 =
|
69 |
+
* Revival
|
70 |
+
* Added feedback system
|
71 |
+
* Updated readme
|
72 |
|
73 |
= 1.4.6 =
|
74 |
* Tested up to WordPress 4.8
|