Version Description
- Plugin revival
- Feedback system integrated
- Readme updated
Download this release
Release Info
Developer | cl272 |
Plugin | Ultimate Posts Widget |
Version | 2.0.8 |
Comparing to | |
See all releases |
Code changes from version 2.0.7 to 2.0.8
- LICENSE.md +0 -7
- README.md +0 -256
- 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
- css/upw-admin.min.css +0 -0
- css/upw-theme-standard.min.css +0 -0
- js/upw-admin.min.js +0 -0
- readme.txt +14 -14
- ultimate-posts-widget.php +11 -3
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,256 +0,0 @@
|
|
1 |
-
# Ultimate Posts Widget
|
2 |
-
|
3 |
-
The ultimate widget for displaying posts, custom post types or sticky posts with an array of options to customize the display.
|
4 |
-
|
5 |
-
Designed for both the average user and developer, Ultimate Posts Widgets aims to provide flexibility and ease of use for displaying any kinds of posts within your widget areas. An array of widget options are available as well as hooks, filters and custom templates for more advanced customization.
|
6 |
-
|
7 |
-
## Options
|
8 |
-
|
9 |
-
* Filter by categories
|
10 |
-
* Filter by current category
|
11 |
-
* Filter by tags
|
12 |
-
* Filter by current tag
|
13 |
-
* Filter by custom post types
|
14 |
-
* Filter by sticky posts
|
15 |
-
* Select number of posts to display
|
16 |
-
* Display title
|
17 |
-
* Display publish date/time with custom format options
|
18 |
-
* Display post author and link
|
19 |
-
* Display post comment count
|
20 |
-
* Display excerpt or full content
|
21 |
-
* Display read more link with custom label
|
22 |
-
* Display featured image and at any size
|
23 |
-
* Display post categories
|
24 |
-
* Display post tags
|
25 |
-
* Display custom fields
|
26 |
-
* Add text or HTML before and after posts list
|
27 |
-
* Add CSS class to widget
|
28 |
-
* Add widget title link
|
29 |
-
* Change excerpt length (in words)
|
30 |
-
* Order by date, title, number of comments, random or a custom field
|
31 |
-
|
32 |
-
## More Information
|
33 |
-
|
34 |
-
* For help use [wordpress.org](http://wordpress.org/support/plugin/ultimate-posts-widget/)
|
35 |
-
* Fork or contribute on [Github](https://github.com/bostondv/ultimate-posts-widget/)
|
36 |
-
* Follow me on [Twitter](http://twitter.com/bostondv/)
|
37 |
-
* View my other [WordPress Plugins](http://profiles.wordpress.org/bostondv/)
|
38 |
-
|
39 |
-
## Support
|
40 |
-
|
41 |
-
Enjoy this plugin? [Send a tip to support development](http://bostondv.com/tips/).
|
42 |
-
|
43 |
-
## Frequently Asked Questions
|
44 |
-
|
45 |
-
#### Filters
|
46 |
-
|
47 |
-
**upw_enqueue_styles** *(boolean)*
|
48 |
-
Allows changing whether to load the template CSS file(s).
|
49 |
-
|
50 |
-
**upw_wp_query_args** *(array)*
|
51 |
-
Allows changing the WP_Query arguments for the widget.
|
52 |
-
|
53 |
-
**upw_custom_template_path** *(string)*
|
54 |
-
Allows changing the default custom template path.
|
55 |
-
|
56 |
-
#### Templates
|
57 |
-
|
58 |
-
**legacy**
|
59 |
-
As of version 2.0.0 we changed the widget HTML markup. For installs prior to 2.0.0 you can use the legacy template to retain the old HTML markup and compatibility.
|
60 |
-
|
61 |
-
**standard**
|
62 |
-
The new standard template as of version 2.0.0. It features better HTML5 markup, improved styling on various themes without the need for custom CSS, and hfeed microformat.
|
63 |
-
|
64 |
-
**custom**
|
65 |
-
As of version 2.0.0 you can now provide your own custom template files. To do this, create a directory named `upw` in your theme and copy a template from the plugin's `templates` directory. Edit as needed.
|
66 |
-
|
67 |
-
Then, in the widget settings under the *Display* tab, choose *Custom* from the *Template* drop down. In the *Custom Template Name* field that appears and enter the file name of your template (excluding .php). For example, if your template is named `custom.php` then enter `custom` in the *Custom Template Name* field.
|
68 |
-
|
69 |
-
#### Images sizes
|
70 |
-
|
71 |
-
*As of version 2.0.0, the plugin no longer supports setting custom image sizes from the widget options panel.*
|
72 |
-
|
73 |
-
To change image sizes you can either edit the built-in sizes (thumbnail, medium, and large) or define a custom image size in your theme `functions.php`.
|
74 |
-
|
75 |
-
**Edit built-in image sizes:** Go to *Settings > Media* and change the image sizes as desired. Once image sizes are changed you will need to regenerate thumbnails to update any existing images. This can be done with [AJAX Thumbnail Rebuild](https://wordpress.org/plugins/ajax-thumbnail-rebuild/) or [Regenerate Thumbnails](https://wordpress.org/plugins/regenerate-thumbnails/) plugins. Note that this will affect image sizes for the entire site.
|
76 |
-
|
77 |
-
**Define a custom image size:** Edit your `functions.php` file and add a new image size with the `add_image_size` function. See the [WordPress codex for documentation](http://codex.wordpress.org/Function_Reference/add_image_size). Once the function is added, your custom size will be available to select from the widget options. Like editing a built-in size, you will need to regenerate thumbnails for existing images.
|
78 |
-
|
79 |
-
Example:
|
80 |
-
|
81 |
-
`<?php add_image_size( 'my-custom-size', 800, 600, false ); ?>`
|
82 |
-
|
83 |
-
#### Thumbnail images are not displaying
|
84 |
-
|
85 |
-
*As of version 2.0.0 timthumb is no longer used.*
|
86 |
-
|
87 |
-
This plugin uses the [timthumb library](http://www.binarymoon.co.uk/projects/timthumb/) to resize post thumbnails. Please review these requirements and troubleshooting tips if you are having problems displaying thumbnails.
|
88 |
-
|
89 |
-
* Right click > view image - If an image isn't loading then this is the first thing you should do. 9 times out of 10 it will tell you what the problem is in an easy to read error message.
|
90 |
-
* JetPack plugin - There is a known conflict between JetPack's "Photon" component, please disable it for compatibility with timthumb.
|
91 |
-
* Server requirements - PHP and the GD image library must be installed on your web server. Normally most web servers include them by default.
|
92 |
-
* Cache permissions - The cache directory `wp-content/plugins/ultimate-posts-widget/cache` should be set to 777 or if that doesn't work, you may find 755 is ok.
|
93 |
-
* Image sizes - timthumb is configured to only work for images smaller than 1500 x 1500. The plugin and automatically selects the "Large" size from Settings > Media, if it is greater than 1500 x 1500 you will need to reduce the size or modify the configuration in `thumb.php` to support larger image sizes.
|
94 |
-
* Tilde(~) in url - timthumb has a known issue with this, please use a url without a tilde until a fix is available. [Bug report](https://code.google.com/p/timthumb/issues/detail?id=263)
|
95 |
-
* Thumbnail images only work with WordPress' native post thumbnail / featured image. Many theme use a custom image field for thumbnails, these are not supported.
|
96 |
-
|
97 |
-
**Still stuck?** See [additional troubleshooting tips](http://www.binarymoon.co.uk/2010/11/timthumb-hints-tips/) from the timthumb author.
|
98 |
-
|
99 |
-
## Screenshots
|
100 |
-
|
101 |
-
1. Example with TwentyTwelve theme with the default widget options
|
102 |
-
2. General options tab
|
103 |
-
3. Display options tab
|
104 |
-
4. Filter options tab
|
105 |
-
5. Order options tab
|
106 |
-
|
107 |
-
## Changelog
|
108 |
-
|
109 |
-
### 2.0.7
|
110 |
-
* Tested update WordPress 4.8
|
111 |
-
|
112 |
-
### 2.0.6
|
113 |
-
* Fix PHP notice (thanks @rhyswynne)
|
114 |
-
* Search custom template both in child theme and in parent (thanks @groucho75)
|
115 |
-
|
116 |
-
### 2.0.5
|
117 |
-
* Adds menu order sort by option
|
118 |
-
|
119 |
-
### 2.0.4
|
120 |
-
* Updated class constructor for WordPress 4.3 compatibility (thanks @JustB)
|
121 |
-
|
122 |
-
### 2.0.3
|
123 |
-
* Adds link to thumbnail images
|
124 |
-
* Adds option to show all categories, tags, or types for better usability
|
125 |
-
* Improve spacing and font sizes on some themes for the standard template
|
126 |
-
* Document adding custom image sizes
|
127 |
-
* Add option to display full size post thumbnail
|
128 |
-
* Remove post_class() from legacy template for better backwards compatibility
|
129 |
-
|
130 |
-
### 2.0.2
|
131 |
-
* Reverts back to using `widget_title` filter
|
132 |
-
* Fixes number of posts setting to allow unlimited amounts
|
133 |
-
* Adds instance and widget ID variables to filters for more control
|
134 |
-
|
135 |
-
### 2.0.1
|
136 |
-
* Fixes issue loading multiple widgets on the same page and using the same template
|
137 |
-
* Adds a default title to the widget for new instances
|
138 |
-
* Documentation improvements
|
139 |
-
|
140 |
-
### 2.0.0
|
141 |
-
|
142 |
-
* Added a new standard template and custom template support
|
143 |
-
* Added basic CSS styles for better formatting
|
144 |
-
* Add hfeed microformat into new standard template
|
145 |
-
* Improved widget options interface
|
146 |
-
* Add option to filter by tags or current tag
|
147 |
-
* Remove timthumb in favor of using built-in WordPress image sizes
|
148 |
-
* Remove widget "More Button" options
|
149 |
-
* Change date display options to now accept any custom date formats
|
150 |
-
* Add before and after posts HTML fields
|
151 |
-
* Add option for widget CSS class (thanks @avenirer)
|
152 |
-
* Add option to display comment count
|
153 |
-
* Set better defaults for newly created widgets
|
154 |
-
* Various bug fixes and optimizations
|
155 |
-
|
156 |
-
*Upgrading from an earlier version?*
|
157 |
-
|
158 |
-
* Upgrades from prior to 2.0.0 we retain the "legacy" template for you. To change which template to use, find the "Template" option under the "Display" tab.
|
159 |
-
* If you used the "More Button", you will need to add in your own HTML into one of the new fields since the more button is now removed.
|
160 |
-
* If you used custom thumbnail settings you will need to update them. You can now choose from pre-defined sizes available to WordPress. If you need an additional image size please see [how to add image sizes](http://codex.wordpress.org/Function_Reference/add_image_size).
|
161 |
-
* Date formatting has changed, you will need to update your settings. By default it uses WordPress' date/time options.
|
162 |
-
|
163 |
-
### 1.9.0
|
164 |
-
|
165 |
-
* Adds option to order by custom field (thanks @enekochan)
|
166 |
-
* Remove "Permalink to:" from link titles (thanks @wirelessgizmo)
|
167 |
-
* Swedish translation (thanks @brstp)
|
168 |
-
|
169 |
-
### 1.8.1
|
170 |
-
|
171 |
-
* Add content display option
|
172 |
-
|
173 |
-
### 1.8
|
174 |
-
|
175 |
-
* Add custom field display
|
176 |
-
* Re-organized widget options
|
177 |
-
* Use proper alt tag for image thumbnails
|
178 |
-
* Better title attribute for links
|
179 |
-
|
180 |
-
### 1.7
|
181 |
-
|
182 |
-
* Added show author option
|
183 |
-
* Added class to highlight current post
|
184 |
-
* Fixed PHP error notices
|
185 |
-
|
186 |
-
### 1.6
|
187 |
-
|
188 |
-
* Added localization support
|
189 |
-
* Added show post categories option
|
190 |
-
* Added show post tags option
|
191 |
-
* Added custom widget title URL option
|
192 |
-
* Improved filter controls for sticky posts
|
193 |
-
* Bug fixes
|
194 |
-
|
195 |
-
###1.5.1
|
196 |
-
|
197 |
-
* Upgrade timthumb to 2.8.11
|
198 |
-
* Load "Large" size post thumbnails which should eliminate large image errors
|
199 |
-
* Improved FAQ for thumbnail troubleshooting
|
200 |
-
|
201 |
-
### 1.5
|
202 |
-
|
203 |
-
* Adds crop mode option
|
204 |
-
* Add show published time option
|
205 |
-
* Now uses date / time format settings
|
206 |
-
* Bug fixes
|
207 |
-
|
208 |
-
### 1.4.5
|
209 |
-
|
210 |
-
* Updates screenshot
|
211 |
-
|
212 |
-
### 1.4.4
|
213 |
-
|
214 |
-
* Fixes show_readmore function
|
215 |
-
* Adds more button link option
|
216 |
-
|
217 |
-
### 1.4
|
218 |
-
|
219 |
-
* Cleaner widget options
|
220 |
-
* Code refactoring and fixes
|
221 |
-
* Adds order by option
|
222 |
-
|
223 |
-
### 1.3
|
224 |
-
|
225 |
-
* Sticky posts only now optional
|
226 |
-
|
227 |
-
### 1.2
|
228 |
-
|
229 |
-
* Added post type filter option.
|
230 |
-
* Code cleanup.
|
231 |
-
* Better selection mechanism for categories.
|
232 |
-
|
233 |
-
### 1.1
|
234 |
-
|
235 |
-
* Added category filter option.
|
236 |
-
|
237 |
-
### 1.0
|
238 |
-
|
239 |
-
* First release.
|
240 |
-
|
241 |
-
## Upgrade Notice
|
242 |
-
|
243 |
-
### 2.0.0
|
244 |
-
|
245 |
-
This is a major update and includes some breaking changes. New templates have been introduced, a few fields have changed, and thumbnails have been modified. See the changelog for details.
|
246 |
-
|
247 |
-
## Installation
|
248 |
-
|
249 |
-
1. Download and extract the zip archive
|
250 |
-
2. Upload `ultimate-posts-widget` folder to `/wp-content/plugins/`
|
251 |
-
3. Activate the plugin through the 'Plugins' menu in WordPress
|
252 |
-
4. Add the widget to a sidebar and configure the options as desired
|
253 |
-
|
254 |
-
## About
|
255 |
-
|
256 |
-
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 |
+
);
|
css/upw-admin.min.css
CHANGED
File without changes
|
css/upw-theme-standard.min.css
CHANGED
File without changes
|
js/upw-admin.min.js
CHANGED
File without changes
|
readme.txt
CHANGED
@@ -1,17 +1,19 @@
|
|
1 |
=== Ultimate Posts Widget ===
|
2 |
-
Contributors:
|
3 |
-
Donate link:
|
4 |
License: MIT
|
5 |
License URI: http://opensource.org/licenses/MIT
|
6 |
Tags: widget, recent posts, custom post types, sticky posts, featured image, post thumbnail, excerpts, category, custom fields, list pages, widget query, microformats, customizable widget, categories widget, tags widget, excerpt, widget templates, post author, post date, custom query, ultimate posts, comments, orderby, comment count
|
7 |
Requires at least: 3.5
|
8 |
-
Tested up to:
|
9 |
-
Stable tag: 2.0.
|
10 |
|
11 |
The ultimate widget for displaying posts, custom post types or sticky posts with an array of options.
|
12 |
|
13 |
== Description ==
|
14 |
|
|
|
|
|
15 |
The ultimate widget for displaying posts, custom post types or sticky posts with an array of options to customize the display.
|
16 |
|
17 |
Designed for both the average user and developer, Ultimate Posts Widgets aims to provide flexibility and ease of use for displaying any kinds of posts within your widget areas. An array of widget options are available as well as hooks, filters and custom templates for more advanced customization.
|
@@ -45,18 +47,11 @@ Designed for both the average user and developer, Ultimate Posts Widgets aims to
|
|
45 |
|
46 |
See the [FAQ tab](https://wordpress.org/plugins/ultimate-posts-widget/faq/) for documentation on custom templates, hooks, common issues, and more.
|
47 |
|
48 |
-
= More Information =
|
49 |
-
|
50 |
-
* For help use [wordpress.org](http://wordpress.org/support/plugin/ultimate-posts-widget/)
|
51 |
-
* Fork or contribute on [Github](https://github.com/bostondv/ultimate-posts-widget/)
|
52 |
-
* Follow me on [Twitter](http://twitter.com/bostondv/)
|
53 |
-
* View my other [WordPress Plugins](http://profiles.wordpress.org/bostondv/)
|
54 |
-
|
55 |
-
Made with <3 by [Boston Dell-Vandenberg](http://bostondv.com).
|
56 |
-
|
57 |
= Support =
|
58 |
|
59 |
-
|
|
|
|
|
60 |
|
61 |
== Frequently Asked Questions ==
|
62 |
|
@@ -124,6 +119,11 @@ This plugin uses the [timthumb library](http://www.binarymoon.co.uk/projects/tim
|
|
124 |
|
125 |
== Changelog ==
|
126 |
|
|
|
|
|
|
|
|
|
|
|
127 |
= 2.0.7 =
|
128 |
* Tested update WordPress 4.8
|
129 |
|
1 |
=== Ultimate Posts Widget ===
|
2 |
+
Contributors: cl272
|
3 |
+
Donate link: https://sellcodes.com/5U4SICyc
|
4 |
License: MIT
|
5 |
License URI: http://opensource.org/licenses/MIT
|
6 |
Tags: widget, recent posts, custom post types, sticky posts, featured image, post thumbnail, excerpts, category, custom fields, list pages, widget query, microformats, customizable widget, categories widget, tags widget, excerpt, widget templates, post author, post date, custom query, ultimate posts, comments, orderby, comment count
|
7 |
Requires at least: 3.5
|
8 |
+
Tested up to: 5.3
|
9 |
+
Stable tag: 2.0.8
|
10 |
|
11 |
The ultimate widget for displaying posts, custom post types or sticky posts with an array of options.
|
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 |
The ultimate widget for displaying posts, custom post types or sticky posts with an array of options to customize the display.
|
18 |
|
19 |
Designed for both the average user and developer, Ultimate Posts Widgets aims to provide flexibility and ease of use for displaying any kinds of posts within your widget areas. An array of widget options are available as well as hooks, filters and custom templates for more advanced customization.
|
47 |
|
48 |
See the [FAQ tab](https://wordpress.org/plugins/ultimate-posts-widget/faq/) for documentation on custom templates, hooks, common issues, and more.
|
49 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
50 |
= Support =
|
51 |
|
52 |
+
For help please ask in the [Support Forum](http://wordpress.org/support/plugin/ultimate-posts-widget/)
|
53 |
+
|
54 |
+
Enjoy this plugin? [Send a tip to support development](https://sellcodes.com/5U4SICyc).
|
55 |
|
56 |
== Frequently Asked Questions ==
|
57 |
|
119 |
|
120 |
== Changelog ==
|
121 |
|
122 |
+
= 2.0.8 =
|
123 |
+
* Plugin revival
|
124 |
+
* Feedback system integrated
|
125 |
+
* Readme updated
|
126 |
+
|
127 |
= 2.0.7 =
|
128 |
* Tested update WordPress 4.8
|
129 |
|
ultimate-posts-widget.php
CHANGED
@@ -3,13 +3,21 @@
|
|
3 |
Plugin Name: Ultimate Posts Widget
|
4 |
Plugin URI: http://wordpress.org/plugins/ultimate-posts-widget/
|
5 |
Description: The ultimate widget for displaying posts, custom post types or sticky posts with an array of options.
|
6 |
-
Version: 2.0.
|
7 |
-
Author:
|
8 |
-
Author URI:
|
9 |
Text Domain: upw
|
10 |
Domain Path: /languages/
|
11 |
License: MIT
|
12 |
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
13 |
|
14 |
if ( !class_exists( 'WP_Widget_Ultimate_Posts' ) ) {
|
15 |
|
3 |
Plugin Name: Ultimate Posts Widget
|
4 |
Plugin URI: http://wordpress.org/plugins/ultimate-posts-widget/
|
5 |
Description: The ultimate widget for displaying posts, custom post types or sticky posts with an array of options.
|
6 |
+
Version: 2.0.8
|
7 |
+
Author: Clever Widgets
|
8 |
+
Author URI: https://themecheck.info
|
9 |
Text Domain: upw
|
10 |
Domain Path: /languages/
|
11 |
License: MIT
|
12 |
*/
|
13 |
+
require_once 'analyst/main.php';
|
14 |
+
|
15 |
+
analyst_init(array(
|
16 |
+
'client-id' => 'vmg6q36wn85b8kzr',
|
17 |
+
'client-secret' => '35dcca0d55e95f21b3b1f3c6987ae34cf38c65c5',
|
18 |
+
'base-dir' => __FILE__
|
19 |
+
));
|
20 |
+
|
21 |
|
22 |
if ( !class_exists( 'WP_Widget_Ultimate_Posts' ) ) {
|
23 |
|