WP Broken Link Status Checker - Version 1.0

Version Description

Release Date: February 11th, 2016

  • First and tested released until WordPress 4.4.2
  • Tested code from WordPress 3.4 version.

=

Download this release

Release Info

Developer seedplugins
Plugin Icon 128x128 WP Broken Link Status Checker
Version 1.0
Comparing to
See all releases

Version 1.0

Files changed (64) hide show
  1. admin/admin.php +481 -0
  2. admin/extensions.php +41 -0
  3. admin/scans-submit.php +275 -0
  4. admin/scans.php +936 -0
  5. admin/settings.php +82 -0
  6. assets/css/admin.css +562 -0
  7. assets/images/advanced-anchor.png +0 -0
  8. assets/images/advanced-close.png +0 -0
  9. assets/images/advanced-extended.png +0 -0
  10. assets/images/advanced-filters.png +0 -0
  11. assets/images/advanced-reset.png +0 -0
  12. assets/images/advanced-url.png +0 -0
  13. assets/images/button-close-hover.png +0 -0
  14. assets/images/button-close.png +0 -0
  15. assets/images/close.png +0 -0
  16. assets/images/extensions/pro-banner.jpg +0 -0
  17. assets/images/extensions/wpls-web-crawler-edit.png +0 -0
  18. assets/images/extensions/wpls-web-crawler-results-actions.png +0 -0
  19. assets/images/extensions/wpls-web-crawler-results-advanced.png +0 -0
  20. assets/images/extensions/wpls-web-crawler-url-tools-results.png +0 -0
  21. assets/images/extensions/wpls-web-crawler-url-tools.png +0 -0
  22. assets/images/loading.gif +0 -0
  23. assets/images/scan-clock.png +0 -0
  24. assets/images/scan-launch.png +0 -0
  25. assets/images/scan-link.png +0 -0
  26. assets/images/scan-warning.png +0 -0
  27. assets/images/search.png +0 -0
  28. assets/images/yes.png +0 -0
  29. assets/js/admin-edit.js +277 -0
  30. assets/js/admin.js +138 -0
  31. assets/js/lightboxed/LICENSE +11 -0
  32. assets/js/lightboxed/jquery.lightboxed.js +273 -0
  33. assets/js/lightboxed/jquery.lightboxed.min.js +1 -0
  34. core/alive.php +531 -0
  35. core/boot.php +21 -0
  36. core/crawler.php +2249 -0
  37. core/curl.php +115 -0
  38. core/module.php +195 -0
  39. core/nonce/nonce.php +310 -0
  40. core/notify.php +123 -0
  41. core/plugin.php +118 -0
  42. core/register.php +71 -0
  43. core/requests/class-json.php +960 -0
  44. core/requests/http.php +417 -0
  45. core/scans.php +1918 -0
  46. core/scheme.php +288 -0
  47. core/settings.php +173 -0
  48. core/status.php +212 -0
  49. core/text.php +105 -0
  50. core/types-curl.php +127 -0
  51. core/types.php +752 -0
  52. core/url.php +364 -0
  53. core/util-math.php +36 -0
  54. core/util-string.php +49 -0
  55. core/util.php +114 -0
  56. languages/wp-link-status.pot +2821 -0
  57. readme.txt +79 -0
  58. views/extensions.php +77 -0
  59. views/scans-edit.php +559 -0
  60. views/scans-results.php +1186 -0
  61. views/scans.php +552 -0
  62. views/settings.php +154 -0
  63. views/views.php +78 -0
  64. wp-link-status.php +56 -0
admin/admin.php ADDED
@@ -0,0 +1,481 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Load dependencies
4
+ require_once(dirname(dirname(__FILE__)).'/core/module.php');
5
+
6
+ /**
7
+ * WP Link Status Admin class
8
+ *
9
+ * @package WP Link Status
10
+ * @subpackage WP Link Status Admin
11
+ */
12
+ class WPLNST_Admin extends WPLNST_Core_Module {
13
+
14
+
15
+
16
+ // Properties
17
+ // ---------------------------------------------------------------------------------------------------
18
+
19
+
20
+
21
+ /**
22
+ * Handle submitted scan object
23
+ */
24
+ public $scan_submit;
25
+
26
+
27
+
28
+ /**
29
+ * Version for external calls
30
+ */
31
+ protected $script_version;
32
+
33
+
34
+
35
+ // Initialization
36
+ // ---------------------------------------------------------------------------------------------------
37
+
38
+
39
+
40
+ /**
41
+ * Creates a singleton object
42
+ */
43
+ public static function instantiate($args = null) {
44
+ return self::get_instance(get_class(), $args);
45
+ }
46
+
47
+
48
+
49
+ /**
50
+ * Custom constructor
51
+ */
52
+ protected function on_construct($args = null) {
53
+
54
+ // Dependencies
55
+ wplnst_require('core', 'text');
56
+
57
+ // Load translations
58
+ add_action('plugins_loaded', array('WPLNST_Core_Plugin', 'load_plugin_textdomain'));
59
+
60
+ // Check AJAX Mode
61
+ if (defined('DOING_AJAX') && DOING_AJAX) {
62
+
63
+ // Check this plugin action
64
+ if (!empty($_POST['action']) && 0 === strpos($_POST['action'], 'wplnst_')) {
65
+
66
+ // Check suffix
67
+ $suffix = mb_substr($_POST['action'], 7);
68
+ if (!empty($suffix) && method_exists($this, 'ajax_'.$suffix)) {
69
+
70
+ // Set AJAX handler
71
+ add_action('wp_ajax_'.$_POST['action'], array(&$this, 'ajax_'.$suffix));
72
+ }
73
+ }
74
+
75
+ // Continue
76
+ } else {
77
+
78
+ // Check submit
79
+ $this->scans_edit_submit_check();
80
+
81
+ // Menu
82
+ add_action('admin_menu', array(&$this, 'admin_menu'));
83
+
84
+ // Enqueues
85
+ add_action('admin_enqueue_scripts', array(&$this, 'admin_enqueue'));
86
+
87
+ // Screen options
88
+ add_filter('set-screen-option', array(&$this, 'options_screen_set'), 11, 3);
89
+ }
90
+ }
91
+
92
+
93
+
94
+ /**
95
+ * Enqueue scripts and styles
96
+ */
97
+ public function admin_enqueue() {
98
+
99
+ // Check plugin context
100
+ if (!self::is_plugin_page())
101
+ return;
102
+
103
+ // Compose script version
104
+ $this->script_version = self::get_script_version();
105
+
106
+ // Commmon admin styles
107
+ wp_enqueue_style('wplnst-admin-css', plugins_url('assets/css/admin.css', WPLNST_FILE), array(), $this->script_version);
108
+
109
+ // jQuery Lightboxed plugin
110
+ wp_enqueue_script('wplnst-admin-lighboxed', plugins_url('assets/js/lightboxed/jquery.lightboxed.min.js', WPLNST_FILE), array('jquery'), $this->script_version, true);
111
+
112
+ // Common admin script
113
+ wp_enqueue_script('wplnst-admin-script', plugins_url('assets/js/admin.js', WPLNST_FILE), array('jquery'), $this->script_version, true);
114
+
115
+ // Edit scan script
116
+ if (WPLNST_Core_Plugin::slug.'-new-scan' == $_GET['page'] || (!empty($_GET['context']) && 'edit' == $_GET['context']))
117
+ wp_enqueue_script('wplnst-admin-script-edit', plugins_url('assets/js/admin-edit.js', WPLNST_FILE), array('jquery', 'json2'), $this->script_version, true);
118
+
119
+ // Enqueue version specific scripts
120
+ $this->admin_enqueue_version();
121
+
122
+ // Screen options
123
+ $this->options_screen_add();
124
+ }
125
+
126
+
127
+
128
+ /**
129
+ * Enqueue specific versions scripts
130
+ */
131
+ protected function admin_enqueue_version() {
132
+ // Need to override, but do nothing
133
+ }
134
+
135
+
136
+
137
+ /**
138
+ * Admin menu hooks
139
+ */
140
+ public function admin_menu() {
141
+
142
+ // Base 64 encoded SVG image
143
+ $icon_svg = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Im5vIj8+PHN2ZyAgIHhtbG5zOmRjPSJodHRwOi8vcHVybC5vcmcvZGMvZWxlbWVudHMvMS4xLyIgICB4bWxuczpjYz0iaHR0cDovL2NyZWF0aXZlY29tbW9ucy5vcmcvbnMjIiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIgICB4bWxuczpzdmc9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiAgIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgICB4bWxuczpzb2RpcG9kaT0iaHR0cDovL3NvZGlwb2RpLnNvdXJjZWZvcmdlLm5ldC9EVEQvc29kaXBvZGktMC5kdGQiICAgeG1sbnM6aW5rc2NhcGU9Imh0dHA6Ly93d3cuaW5rc2NhcGUub3JnL25hbWVzcGFjZXMvaW5rc2NhcGUiICAgdmVyc2lvbj0iMS4xIiAgIHg9IjBweCIgICB5PSIwcHgiICAgdmlld0JveD0iMCAwIDEwMCAxMDAiICAgZW5hYmxlLWJhY2tncm91bmQ9Im5ldyAwIDAgMTAwIDEwMCIgICB4bWw6c3BhY2U9InByZXNlcnZlIiAgIGlkPSJzdmcyIiAgIGlua3NjYXBlOnZlcnNpb249IjAuNDguNCByOTkzOSIgICB3aWR0aD0iMTAwJSIgICBoZWlnaHQ9IjEwMCUiICAgc29kaXBvZGk6ZG9jbmFtZT0ibGluay1zdGF0dXMtNy5zdmciPjxtZXRhZGF0YSAgICAgaWQ9Im1ldGFkYXRhMTAiPjxyZGY6UkRGPjxjYzpXb3JrICAgICAgICAgcmRmOmFib3V0PSIiPjxkYzpmb3JtYXQ+aW1hZ2Uvc3ZnK3htbDwvZGM6Zm9ybWF0PjxkYzp0eXBlICAgICAgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIiAvPjwvY2M6V29yaz48L3JkZjpSREY+PC9tZXRhZGF0YT48ZGVmcyAgICAgaWQ9ImRlZnM4IiAvPjxzb2RpcG9kaTpuYW1lZHZpZXcgICAgIHBhZ2Vjb2xvcj0iI2ZmZmZmZiIgICAgIGJvcmRlcmNvbG9yPSIjNjY2NjY2IiAgICAgYm9yZGVyb3BhY2l0eT0iMSIgICAgIG9iamVjdHRvbGVyYW5jZT0iMTAiICAgICBncmlkdG9sZXJhbmNlPSIxMCIgICAgIGd1aWRldG9sZXJhbmNlPSIxMCIgICAgIGlua3NjYXBlOnBhZ2VvcGFjaXR5PSIwIiAgICAgaW5rc2NhcGU6cGFnZXNoYWRvdz0iMiIgICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTY4MCIgICAgIGlua3NjYXBlOndpbmRvdy1oZWlnaHQ9Ijk3NCIgICAgIGlkPSJuYW1lZHZpZXc2IiAgICAgc2hvd2dyaWQ9ImZhbHNlIiAgICAgaW5rc2NhcGU6em9vbT0iMi4zNiIgICAgIGlua3NjYXBlOmN4PSItMjguNjAxNjk1IiAgICAgaW5rc2NhcGU6Y3k9IjUwIiAgICAgaW5rc2NhcGU6d2luZG93LXg9IjAiICAgICBpbmtzY2FwZTp3aW5kb3cteT0iMjQiICAgICBpbmtzY2FwZTp3aW5kb3ctbWF4aW1pemVkPSIxIiAgICAgaW5rc2NhcGU6Y3VycmVudC1sYXllcj0ic3ZnMiIgLz48cGF0aCAgICAgZD0iTTUwLDBDMjIuNCwwLDAsMjIuNCwwLDUwczIyLjQsNTAsNTAsNTBzNTAtMjIuNCw1MC01MFM3Ny42LDAsNTAsMHogTTU1LjgsNjUuM0w0NCw3N2MtMy4yLDMuMi03LjUsNS0xMS44LDVoMCAgYy0zLjksMC03LjUtMS41LTEwLjItNC4xYy01LjgtNS44LTUuOC0xNS4zLDAtMjEuMWwxNC4xLTE0LjFMMzYsNDQuOWMtMC4yLDIuNiwwLjEsNS4xLDAuOSw3LjRsMC4yLDAuNWwtOS41LDkuNSAgYy0xLjQsMS40LTIuMSwzLjItMi4xLDUuMWMwLDEuOSwwLjcsMy43LDIuMSw1LjFjMS4yLDEuMiwyLjksMS45LDQuNywxLjljMi4zLDAsNC42LTEsNi4zLTIuN2wxMS43LTExLjdjMy4zLTMuMywzLjctOC4yLDAuOS0xMSAgYy0wLjctMC43LTEtMS45LTAuOC0zLjFjMC4yLTEuMiwwLjgtMi4zLDEuNy0zLjJsMS41LTEuNWwwLjUsMC4zYzAuOSwwLjYsMS44LDEuMywyLjUsMmMwLjgsMC44LDEuNCwxLjYsMiwyLjYgIEM2Mi4zLDUxLjksNjEuMSw2MCw1NS44LDY1LjN6IE03Ny45LDQzLjJMNjMuOCw1Ny4zbDAuMi0yLjFjMC4yLTIuNi0wLjEtNS4xLTAuOS03LjRMNjMsNDcuMmw5LjUtOS41YzEuNC0xLjQsMi4xLTMuMiwyLjEtNS4xICBjMC0xLjktMC43LTMuNy0yLjEtNS4xYy0xLjItMS4yLTIuOS0xLjktNC43LTEuOWMtMi4zLDAtNC42LDEtNi4zLDIuN0w0OS43LDQwLjFjLTMuMywzLjMtMy43LDguMi0wLjksMTFjMC43LDAuNywxLDEuOSwwLjgsMy4xICBjLTAuMiwxLjItMC44LDIuMy0xLjcsMy4yTDQ2LjQsNTlsLTAuNS0wLjNjLTAuOS0wLjYtMS44LTEuMy0yLjUtMmMtMC44LTAuOC0xLjQtMS42LTItMi42Yy0zLjYtNS45LTIuNC0xNC4xLDIuOS0xOS40TDU2LDIzICBjMy4yLTMuMiw3LjUtNSwxMS44LTVjMy45LDAsNy41LDEuNSwxMC4yLDQuMWMyLjgsMi44LDQuNCw2LjYsNC40LDEwLjVDODIuMywzNi42LDgwLjcsNDAuNCw3Ny45LDQzLjJ6IiAgICAgaWQ9InBhdGg0IiAgICAgc3R5bGU9ImZpbGw6I2ZmZmZmZjtmaWxsLW9wYWNpdHk6MSIgLz48L3N2Zz4=';
144
+
145
+ // Main menu
146
+ add_menu_page($this->get_plugin_title(), $this->get_menu_title(), WPLNST_Core_Plugin::capability, WPLNST_Core_Plugin::slug, array(&$this, 'admin_menu_scans'), $icon_svg, '99.20226');
147
+
148
+ // Scans
149
+ add_submenu_page(WPLNST_Core_Plugin::slug, self::get_text('scans'), self::get_text('scans'), WPLNST_Core_Plugin::capability, WPLNST_Core_Plugin::slug, array(&$this, 'admin_menu_scans'));
150
+ add_submenu_page(WPLNST_Core_Plugin::slug, self::get_text('scan_new'), self::get_text('scan_new'), WPLNST_Core_Plugin::capability, WPLNST_Core_Plugin::slug.'-new-scan', array(&$this, 'admin_menu_scans_new'));
151
+
152
+ // Utilities
153
+ $this->admin_menu_utilities();
154
+
155
+ // Settings
156
+ add_submenu_page(WPLNST_Core_Plugin::slug, self::get_text('settings'), self::get_text('settings'), WPLNST_Core_Plugin::capability, WPLNST_Core_Plugin::slug.'-settings', array(&$this, 'admin_menu_settings'));
157
+
158
+ // Addons
159
+ $this->admin_menu_addons();
160
+ }
161
+
162
+
163
+
164
+ /**
165
+ * Admin menu utilities
166
+ */
167
+ protected function admin_menu_utilities() {}
168
+
169
+
170
+
171
+ /**
172
+ * Admin menu addons
173
+ */
174
+ protected function admin_menu_addons() {
175
+ do_action('wplnst_admin_menu_addons');
176
+ add_submenu_page(WPLNST_Core_Plugin::slug, __('Extensions', 'wplnst'), '<span style="color:#f18500">'.__('Extensions', 'wplnst').'</span>', WPLNST_Core_Plugin::capability, WPLNST_Core_Plugin::slug.'-extensions', array(&$this, 'admin_menu_extensions'));
177
+ }
178
+
179
+
180
+
181
+ // Menu hooks
182
+ // ---------------------------------------------------------------------------------------------------
183
+
184
+
185
+
186
+ /**
187
+ * Scans common page
188
+ */
189
+ public function admin_menu_scans() {
190
+ wplnst_require('admin', 'scans');
191
+ new WPLNST_Admin_Scans($this, 'context');
192
+ }
193
+
194
+
195
+
196
+ /*
197
+ * New or edit scan page
198
+ */
199
+ public function admin_menu_scans_new() {
200
+ wplnst_require('admin', 'scans');
201
+ new WPLNST_Admin_Scans($this, 'edit');
202
+ }
203
+
204
+
205
+
206
+ /**
207
+ * Settings page
208
+ */
209
+ public function admin_menu_settings() {
210
+ wplnst_require('admin', 'settings');
211
+ new WPLNST_Admin_Settings($this);
212
+ }
213
+
214
+
215
+
216
+ /**
217
+ * Extensions page
218
+ */
219
+ public function admin_menu_extensions() {
220
+ wplnst_require('admin', 'extensions');
221
+ new WPLNST_Admin_Extensions($this);
222
+ }
223
+
224
+
225
+
226
+ // New or edit scan submit handler
227
+ // ---------------------------------------------------------------------------------------------------
228
+
229
+
230
+
231
+ /**
232
+ * Check a submit attempt
233
+ */
234
+ private function scans_edit_submit_check() {
235
+
236
+ // Check plugin context
237
+ if (!self::is_plugin_page())
238
+ return;
239
+
240
+ // Edit scan form, early check because maybe do a redirection for new scans
241
+ if ((WPLNST_Core_Plugin::slug == $_GET['page'] && !empty($_GET['context']) && 'edit' == $_GET['context']) || WPLNST_Core_Plugin::slug.'-new-scan' == $_GET['page']) {
242
+
243
+ // Check id and nonce submit
244
+ if (isset($_POST['scan_id']) && isset($_POST['scan_edit_nonce']))
245
+ add_action('init', array(&$this, 'scans_edit_submit'));
246
+ }
247
+ }
248
+
249
+
250
+
251
+ /**
252
+ * Check scan edit submit and save data
253
+ */
254
+ public function scans_edit_submit() {
255
+ $this->load_scans_object();
256
+ wplnst_require('admin', 'scans-submit');
257
+ $this->scan_submit = new WPLNST_Admin_Scans_Submit($this->scans);
258
+ }
259
+
260
+
261
+
262
+ // Options screen
263
+ // ---------------------------------------------------------------------------------------------------
264
+
265
+
266
+
267
+ /**
268
+ * Add options to the admin screen
269
+ */
270
+ private function options_screen_add() {
271
+
272
+ // Check scans context
273
+ if (WPLNST_Core_Plugin::slug == $_GET['page']) {
274
+
275
+ // Scans list
276
+ if (!isset($_GET['scan_id']) && empty($_GET['context'])) {
277
+ $option_default = WPLNST_Core_Types::scans_per_page;
278
+ $option_per_page = 'wplnst_scans_per_page';
279
+ $option_label = __('Scans per page', 'wplnst');
280
+
281
+ // Results list
282
+ } elseif (isset($_GET['context']) && 'results' == $_GET['context']) {
283
+ $option_default = WPLNST_Core_Types::scans_results_per_page;
284
+ $option_per_page = 'wplnst_scan_results_per_page';
285
+ $option_label = __('Crawler results per page', 'wplnst');
286
+ }
287
+ }
288
+
289
+ // Check option result
290
+ if (isset($option_label)) {
291
+ add_screen_option('per_page', array(
292
+ 'default' => $option_default,
293
+ 'label' => $option_label,
294
+ 'option' => $option_per_page,
295
+ ));
296
+ }
297
+ }
298
+
299
+
300
+
301
+ /**
302
+ * Set the screen options
303
+ */
304
+ public function options_screen_set($status, $option, $value) {
305
+
306
+ // Options names
307
+ $allowed = array(
308
+ 'scans_per_page',
309
+ 'scan_results_per_page',
310
+ );
311
+
312
+ // Enum allowed
313
+ foreach ($allowed as $name) {
314
+ if ('wplnst_'.$name == $option)
315
+ return $value;
316
+ }
317
+ }
318
+
319
+
320
+
321
+ // Default view and exceptions
322
+ // ---------------------------------------------------------------------------------------------------
323
+
324
+
325
+
326
+ /**
327
+ * Show default admin view
328
+ */
329
+ public function screen_view($args) {
330
+
331
+ // Set plugin title
332
+ $args['plugin_title'] = $this->get_plugin_title();
333
+
334
+ // Before the screen output
335
+ $this->screen_view_before();
336
+
337
+ // And show it
338
+ self::screen_view_output($args);
339
+ }
340
+
341
+
342
+
343
+ /**
344
+ * Before the screen view output
345
+ */
346
+ protected function screen_view_before() {
347
+ // Need to override, but do nothing
348
+ }
349
+
350
+
351
+
352
+ /**
353
+ * Output the screen view
354
+ */
355
+ private static function screen_view_output($args) {
356
+
357
+ // Vars
358
+ extract($args);
359
+
360
+ ?><div class="wrap wplnst-wrap">
361
+
362
+ <h2 id="wplnst-title"><?php echo empty($title)? '' : $title.' - '; echo $plugin_title; if (!empty($add_item_text) && !empty($add_item_url)) : ?> <a class="add-new-h2" href="<?php echo esc_url($add_item_url); ?>"><?php echo esc_html($add_item_text); ?></a><?php endif; ?></h2>
363
+
364
+ <?php if (!wplnst_is_curl_enabled()) : ?><div class="error notice"><p><?php _e('Not detected the required cURL module enabled. Please contact with your hosting provider in order to install cURL for PHP in this server.', 'wplnst'); ?></p></div><?php endif; ?>
365
+
366
+ <?php if (!empty($notice_error)) : ?><div class="error notice"><p><?php echo $notice_error; ?></p></div><?php endif; ?>
367
+
368
+ <?php if (!empty($notice_success)) : ?><div class="updated notice is-dismissible"><p><?php echo $notice_success; ?></p></div><?php endif; ?>
369
+
370
+ <?php if (!empty($notice_warning)) : ?><div class="notice notice-warning is-dismissible"><p><?php echo $notice_warning; ?></p></div><?php endif; ?>
371
+
372
+ <?php if (!empty($notice_crawler)) : ?><div class="updated notice is-dismissible"><p><?php echo $notice_crawler; ?></p></div><?php endif; ?>
373
+
374
+ <?php if (!empty($wp_action)) do_action($wp_action, $args); ?>
375
+
376
+ </div><?php
377
+ }
378
+
379
+
380
+
381
+ /**
382
+ * Common scan not found
383
+ */
384
+ public function screen_scan_not_found($title) {
385
+ $this->screen_view(array(
386
+ 'title' => $title,
387
+ 'notice_error' => self::get_text('scan_not_found'),
388
+ ));
389
+ }
390
+
391
+
392
+
393
+ /**
394
+ * Invalid data screen
395
+ */
396
+ public function screen_invalid_data($title) {
397
+ $this->screen_view(array(
398
+ 'title' => $title,
399
+ 'notice_error' => self::get_text('invalid_data'),
400
+ ));
401
+ }
402
+
403
+
404
+
405
+ /**
406
+ * Invalid data screen
407
+ */
408
+ public function screen_invalid_nonce($title) {
409
+ $this->screen_view(array(
410
+ 'title' => $title,
411
+ 'notice_error' => self::get_text('invalid_nonce'),
412
+ ));
413
+ }
414
+
415
+
416
+
417
+ /**
418
+ * Common scan not found
419
+ */
420
+ public function screen_unknown_error($title) {
421
+ $this->screen_view(array(
422
+ 'title' => $title,
423
+ 'notice_error' => self::get_text('unknown_error'),
424
+ ));
425
+ }
426
+
427
+
428
+
429
+ // Utilities
430
+ // ---------------------------------------------------------------------------------------------------
431
+
432
+
433
+
434
+ /**
435
+ * Wrapper of WPLNST_Core_Text class get_text method
436
+ */
437
+ public static function get_text($name) {
438
+ return WPLNST_Core_Text::get_text($name);
439
+ }
440
+
441
+
442
+
443
+ /**
444
+ * Return suffixed version for external scripts
445
+ */
446
+ protected function get_script_version() {
447
+ global $wp_version;
448
+ return (empty($wp_version)? '' : 'wp-'.str_replace('http://', '', esc_url($wp_version)).'-').'wplnst-'.WPLNST_VERSION;
449
+ }
450
+
451
+
452
+
453
+ /**
454
+ * Return plugin title in admin menu
455
+ */
456
+ protected function get_menu_title() {
457
+ return WPLNST_Core_Plugin::title;
458
+ }
459
+
460
+
461
+
462
+ /**
463
+ * Return plugin title for default view
464
+ */
465
+ protected function get_plugin_title() {
466
+ return WPLNST_Core_Plugin::title;
467
+ }
468
+
469
+
470
+
471
+ /**
472
+ * Check if we are on any page of this plugin
473
+ */
474
+ private static function is_plugin_page() {
475
+ global $pagenow;
476
+ return !(empty($pagenow) || 'admin.php' != $pagenow || empty($_GET['page']) || 0 !== strpos($_GET['page'], WPLNST_Core_Plugin::slug));
477
+ }
478
+
479
+
480
+
481
+ }
admin/extensions.php ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * WP Link Status Admin Extensions class
5
+ *
6
+ * @package WP Link Status
7
+ * @subpackage WP Link Status Admin
8
+ */
9
+ class WPLNST_Admin_Extensions {
10
+
11
+
12
+
13
+ /**
14
+ * Constructor
15
+ */
16
+ public function __construct(&$admin) {
17
+
18
+ // Custom action view
19
+ add_action('wplnst_view_extensions', array(&$this, 'view_extensions'));
20
+
21
+ // Show settings screen
22
+ $admin->screen_view(array(
23
+ 'title' => __('Extensions', 'wplnst'),
24
+ 'wp_action' => 'wplnst_view_extensions',
25
+ 'action' => WPLNST_Core_Plugin::get_url_extensions(),
26
+ ));
27
+ }
28
+
29
+
30
+
31
+ /**
32
+ * Extension view for settings page
33
+ */
34
+ public function view_extensions($args) {
35
+ wplnst_require('views', 'extensions');
36
+ WPLNST_Views_Extensions::view($args);
37
+ }
38
+
39
+
40
+
41
+ }
admin/scans-submit.php ADDED
@@ -0,0 +1,275 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * WP Link Status Admin Scans Submit class
5
+ *
6
+ * @package WP Link Status
7
+ * @subpackage WP Link Status Admin
8
+ */
9
+ class WPLNST_Admin_Scans_Submit {
10
+
11
+
12
+
13
+ /**
14
+ * Notice updated and error
15
+ */
16
+ public $notice_error = false;
17
+ public $notice_warning = false;
18
+ public $notice_success = false;
19
+ public $notice_crawler = false;
20
+
21
+
22
+
23
+ /**
24
+ * Handle submit in the constructor
25
+ */
26
+ public function __construct($scans) {
27
+
28
+ // Globals
29
+ global $wpdb;
30
+
31
+ // Check scan identifier
32
+ $scan_id = isset($_POST['scan_id'])? (int) $_POST['scan_id'] : false;
33
+ if (false === $scan_id || (isset($_GET['scan_id']) && $scan_id != (int) $_GET['scan_id'])) {
34
+ $this->notice_error = WPLNST_Admin::get_text('invalid_data');
35
+ return;
36
+ }
37
+
38
+ // Check existing scan
39
+ if ($scan_id > 0 && false === ($scan = $scans->get_scan_by_id($scan_id))) {
40
+ $this->notice_error = WPLNST_Admin::get_text('scan_not_found');
41
+ return;
42
+ }
43
+
44
+ // Check submitted nonce
45
+ $nonce = isset($_POST['scan_edit_nonce'])? $_POST['scan_edit_nonce'] : false;
46
+ if (false === $nonce || !wp_verify_nonce($nonce, 'scan-edit-'.(empty($scan)? '0' : $scan->hash))) {
47
+ $this->notice_error = WPLNST_Admin::get_text('invalid_nonce');
48
+ return;
49
+ }
50
+
51
+ // Collect data
52
+ $max_threads = isset($_POST['tx-threads'])? (int) $_POST['tx-threads'] : 0;
53
+ $connect_timeout = isset($_POST['tx-connect-timeout'])? (int) $_POST['tx-connect-timeout'] : 0;
54
+ $request_timeout = isset($_POST['tx-request-timeout'])? (int) $_POST['tx-request-timeout'] : 0;
55
+
56
+ // Update data array
57
+ $updates = array(
58
+ 'modified_by' => get_current_user_id(),
59
+ 'modified_at' => current_time('mysql', true),
60
+ 'name' => isset($_POST['tx-name'])? substr(trim(stripslashes($_POST['tx-name'])), 0, 255) : '',
61
+ 'max_threads' => $max_threads,
62
+ 'connect_timeout' => $connect_timeout,
63
+ 'request_timeout' => $request_timeout,
64
+ );
65
+
66
+
67
+ // Initialize
68
+ $config = array();
69
+
70
+
71
+ /* Notifications */
72
+
73
+ // Empty of not completed
74
+ if (empty($scan) || 'end' != $scan->status) {
75
+ $config['notify_default'] = WPLNST_Core_Types::check_post_value('ck-notify-default', 'on', false);
76
+ $config['notify_address'] = WPLNST_Core_Types::check_post_value('ck-notify-address', 'on', false);
77
+ $config['notify_address_email'] = isset($_POST['tx-notify-address-email'])? substr(trim(stripslashes($_POST['tx-notify-address-email'])), 0, 255) : '';
78
+ }
79
+
80
+
81
+ /* Editable scan */
82
+
83
+ // Editable fields
84
+ if (empty($scan) || 'wait' == $scan->status) {
85
+
86
+ // General tab
87
+ $config['destination_type'] = WPLNST_Core_Types::check_post_value('sl-destination-type', array_keys(WPLNST_Core_Types::get_destination_types()), 'all');
88
+ $config['time_scope'] = WPLNST_Core_Types::check_post_value('sl-time-scope', array_keys(WPLNST_Core_Types::get_time_scopes()), 'anytime');
89
+ $config['link_types'] = WPLNST_Core_Types::check_post_value('ck-link-type', array_keys(WPLNST_Core_Types::get_link_types()), array());
90
+ $config['crawl_order'] = WPLNST_Core_Types::check_post_value('sl-crawl-order', array_keys(WPLNST_Core_Types::get_crawl_order()), 'asc');
91
+ $config['redir_status'] = WPLNST_Core_Types::check_post_value('ck-redir-status', 'on', false);
92
+ $config['malformed'] = WPLNST_Core_Types::check_post_value('ck-malformed-links', 'on', false);
93
+
94
+ // Content options tab
95
+ $config['post_types'] = WPLNST_Core_Types::check_post_value('ck-post-type', array_keys(WPLNST_Core_Types::get_post_types()), array());
96
+ $config['post_status'] = WPLNST_Core_Types::check_post_value('ck-post-status', array_keys(WPLNST_Core_Types::get_post_status()), array());
97
+ $config['comment_types'] = WPLNST_Core_Types::check_post_value('ck-comment-type', array_keys(WPLNST_Core_Types::get_comment_types()), array());
98
+ $config['blogroll'] = WPLNST_Core_Types::check_post_value('ck-blogroll', 'on', false);
99
+
100
+ // Links status tab
101
+ $config['status_levels'] = WPLNST_Core_Types::check_post_value('ck-status-level', array_keys(WPLNST_Core_Types::get_status_levels()), array());
102
+ $config['status_codes'] = WPLNST_Core_Types::check_post_value('ck-status-code', array_keys(WPLNST_Core_Types::get_status_codes_raw()), array());
103
+
104
+ // Filters
105
+ $config['custom_fields'] = WPLNST_Core_Types::check_post_elist('scan_custom_fields');
106
+ $config['anchor_filters'] = WPLNST_Core_Types::check_post_elist('scan_anchor_filters');
107
+ $config['include_urls'] = WPLNST_Core_Types::check_post_elist('scan_include_urls');
108
+ $config['exclude_urls'] = WPLNST_Core_Types::check_post_elist('scan_exclude_urls');
109
+ $config['html_attributes'] = WPLNST_Core_Types::check_post_elist('scan_html_attributes');
110
+ $config['filtered_query'] = WPLNST_Core_Types::check_post_value('ck-filtered-query', 'on', false);
111
+ }
112
+
113
+ // Add to update
114
+ if (!empty($config))
115
+ $updates['config'] = @json_encode($config);
116
+
117
+
118
+ /* Save and run */
119
+
120
+ // Check run attempt
121
+ $do_play = (isset($_POST['scan_run']) && 1 == (int) $_POST['scan_run']);
122
+
123
+ // Abort when cURL is not enabled
124
+ if ($do_play && !wplnst_is_curl_enabled())
125
+ $do_play = false;
126
+
127
+ // Check new scan
128
+ if (empty($scan_id)) {
129
+
130
+ // Create unique random hash associated with the scan
131
+ $hash = md5(rand(0, 9999).microtime().rand(0, 9999));
132
+
133
+ // Add scan and redirect
134
+ $wpdb->insert($wpdb->prefix.'wplnst_scans', array_merge($updates, array('status' => 'wait', 'hash' => $hash, 'created_at' => current_time('mysql', true))));
135
+
136
+ // New identifier
137
+ $insert_id = (int) $wpdb->insert_id;
138
+
139
+ // Check error
140
+ if (empty($insert_id)) {
141
+
142
+ // Message error
143
+ $this->notice_error = __('Something went wrong adding the new scan. Please <a href="javascript:history.back();">go back</a> and attempt again to submit form.');
144
+
145
+ // Scan added
146
+ } else {
147
+
148
+ // Run scan
149
+ if ($do_play) {
150
+
151
+ // Check max scans running
152
+ if (!$scans->can_play_more_scans()) {
153
+
154
+ // Maximum reached
155
+ $started = 'max_scans';
156
+
157
+ // Queue scan
158
+ $scans->queue_scan($insert_id);
159
+
160
+ // Retrieve scan
161
+ } elseif (false === ($scan = $scans->get_scan_by_id($insert_id))) {
162
+
163
+ // Can`t start scan
164
+ $started = 'error';
165
+
166
+ // Check ready
167
+ } elseif (true === $scans->is_scan_ready($scan)) {
168
+
169
+ // Update scan ready
170
+ $scans->update_scan_ready($scan->id, true);
171
+
172
+ // Attempt to run scan
173
+ if (false === $scans->play_scan($scan->id)) {
174
+
175
+ // Can`t start scan
176
+ $started = 'error';
177
+
178
+ // Running
179
+ } else {
180
+
181
+ // Done
182
+ $started = 'on';
183
+
184
+ // Save default threads values
185
+ $scans->set_scan_final_threads_options($scan->id, array(
186
+ 'max_threads' => $max_threads,
187
+ 'connect_timeout' => $connect_timeout,
188
+ 'request_timeout' => $request_timeout,
189
+ ));
190
+
191
+ // Attemp to run scan
192
+ WPLNST_Core_Alive::run($scan->id, $hash);
193
+ }
194
+ }
195
+ }
196
+
197
+ // Redirect to scan edit permalink
198
+ wp_redirect(WPLNST_Core_Plugin::get_url_scans_edit($insert_id, true, $do_play? $started : false));
199
+
200
+ // End
201
+ die;
202
+ }
203
+
204
+ // Update scan
205
+ } else {
206
+
207
+ // Update threads values if scan is in play mode
208
+ if ('play' == $scan->status) {
209
+ $updates['max_threads'] = wplnst_get_nsetting('max_threads', $max_threads);
210
+ $updates['connect_timeout'] = wplnst_get_nsetting('connect_timeout', $connect_timeout);
211
+ $updates['request_timeout'] = wplnst_get_nsetting('request_timeout', $request_timeout);
212
+ }
213
+
214
+ // Update scan data
215
+ $rows = $wpdb->update($wpdb->prefix.'wplnst_scans', $updates, array('scan_id' => $scan->id));
216
+ if (empty($rows) || false === ($scan = $scans->get_scan_by_id($scan->id))) {
217
+
218
+ // Message error
219
+ $this->notice_error = __('Something went wrong updating the scan data. Please <a href="javascript:history.back();">go back</a> and attempt again to save data.', 'wplnst');
220
+
221
+ // Done
222
+ } else {
223
+
224
+ // Update message
225
+ $this->notice_success = __('Scan updated successfully.', 'wplnst');
226
+
227
+ // Check if run the crawler, only for waiting scans
228
+ if ($do_play && 'wait' == $scan->status) {
229
+
230
+ // Check max scans running
231
+ if (!$scans->can_play_more_scans()) {
232
+
233
+ // Maximum reached
234
+ $this->notice_warning = WPLNST_Admin::get_text('max_scans');
235
+
236
+ // Queue scan
237
+ $scans->queue_scan($scan->id);
238
+
239
+ // Set to play
240
+ } elseif (false === $scans->play_scan($scan->id)) {
241
+
242
+ // Crawler not started
243
+ $this->notice_warning = sprintf(__('Something went wrong trying to start the crawler.'));
244
+
245
+ // Done
246
+ } else {
247
+
248
+ // Save default threads values
249
+ $scans->set_scan_final_threads_options($scan->id, array(
250
+ 'max_threads' => $max_threads,
251
+ 'connect_timeout' => $connect_timeout,
252
+ 'request_timeout' => $request_timeout,
253
+ ));
254
+
255
+ // Run scan
256
+ WPLNST_Core_Alive::run($scan->id, $scan->hash);
257
+
258
+ // Update message
259
+ $this->notice_crawler = sprintf(__('The crawler for this scan is running. You can see its data in the <a href="%s">crawler results page</a>.', 'wplnst'), esc_url(WPLNST_Core_Plugin::get_url_scans_results($scan->id)));
260
+ }
261
+
262
+ // Not play
263
+ } elseif ('wait' == $scan->status && $scans->can_play_more_scans() && true === $scans->is_scan_ready($scan)) {
264
+
265
+ // Start crawler reminder
266
+ $start_url = esc_url(WPLNST_Core_Plugin::get_url_scans_crawler($scan->id, 'on', $scan->hash));
267
+ $this->notice_success .= ' '.sprintf(__('The crawler for this scan is <strong>not started</strong>, you can <a href="%s">start the crawler</a> now.', 'wplnst'), $start_url);
268
+ }
269
+ }
270
+ }
271
+ }
272
+
273
+
274
+
275
+ }
admin/scans.php ADDED
@@ -0,0 +1,936 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * WP Link Status Admin Scans class
5
+ *
6
+ * @package WP Link Status
7
+ * @subpackage WP Link Status Admin
8
+ */
9
+ class WPLNST_Admin_Scans {
10
+
11
+
12
+
13
+ // Properties
14
+ // ---------------------------------------------------------------------------------------------------
15
+
16
+
17
+
18
+ /**
19
+ * Parent object
20
+ */
21
+ private $admin;
22
+
23
+
24
+
25
+ // Initialization
26
+ // ---------------------------------------------------------------------------------------------------
27
+
28
+
29
+
30
+ /**
31
+ * Constructor
32
+ */
33
+ public function __construct(&$admin, $menu) {
34
+
35
+ // Copy parent
36
+ $this->admin = &$admin;
37
+
38
+ // Scans menu
39
+ if ('context' == $menu) {
40
+ $this->scans_context();
41
+
42
+ // Check edit scan menu
43
+ } elseif ('edit' == $menu) {
44
+ $this->scans_edit();
45
+ }
46
+ }
47
+
48
+
49
+
50
+ /**
51
+ * Scans menu
52
+ */
53
+ private function scans_context() {
54
+
55
+ // Check context
56
+ $context = empty($_GET['context'])? false : $_GET['context'];
57
+
58
+ // Check scan id parameter
59
+ $scan_id = empty($_GET['scan_id'])? 0 : (int) $_GET['scan_id'];
60
+
61
+ // Results context
62
+ if ('results' == $context) {
63
+
64
+ // Crawl results
65
+ $this->scans_results($scan_id);
66
+
67
+ // Crawling order
68
+ } elseif ('crawler' == $context) {
69
+
70
+ // Crawl check
71
+ $this->scans_crawler($scan_id);
72
+
73
+ // Delete context
74
+ } elseif ('delete' == $context) {
75
+
76
+ // Delete confirm
77
+ $this->scans_delete($scan_id);
78
+
79
+ // Edit
80
+ } elseif ('edit' == $context) {
81
+
82
+ // Show edit view
83
+ $this->scans_edit($scan_id);
84
+
85
+ // Default
86
+ } else {
87
+
88
+ // List of scans
89
+ $this->scans_list();
90
+ }
91
+ }
92
+
93
+
94
+
95
+ // Scans list
96
+ // ---------------------------------------------------------------------------------------------------
97
+
98
+
99
+
100
+ /**
101
+ * Prepare the list of scans
102
+ */
103
+ private function scans_list() {
104
+
105
+ // Retrieve scans
106
+ $scans = $this->admin->get_scans(array(
107
+ 'setup_names' => true,
108
+ 'order_by' => 'FIELD(status, "play", "wait", "queued", "stop", "end"), started_at DESC, enqueued_at ASC, created_at DESC',
109
+ 'paged' => empty($_GET['paged'])? 0 : (int) $_GET['paged'],
110
+ ));
111
+
112
+ // No isolated mode
113
+ $scans->isolated = false;
114
+
115
+ // Custom action view
116
+ add_action('wplnst_scans_list_view', array(&$this, 'scans_list_view'));
117
+
118
+ // Show admin screen
119
+ $this->admin->screen_view(array(
120
+ 'scans' => $scans,
121
+ 'wp_action' => 'wplnst_scans_list_view',
122
+ 'add_item_text' => WPLNST_Admin::get_text('scan_new_add'),
123
+ 'add_item_url' => WPLNST_Core_Plugin::get_url_scans_add(),
124
+ ));
125
+ }
126
+
127
+
128
+
129
+ /**
130
+ * Extension view to display the scans list
131
+ */
132
+ public function scans_list_view($args) {
133
+
134
+ // Dependencies
135
+ wplnst_require('views', 'scans');
136
+
137
+ // Display table
138
+ $list = new WPLNST_Views_Scans($args['scans']);
139
+ $list->prepare_items();
140
+ $list->display();
141
+ }
142
+
143
+
144
+
145
+ // Scan results
146
+ // ---------------------------------------------------------------------------------------------------
147
+
148
+
149
+
150
+ /**
151
+ * Prepare and display crawling results
152
+ */
153
+ private function scans_results($scan_id) {
154
+
155
+ // Atempt to load scan
156
+ if (false === ($scan = $this->admin->get_scan_by_id($scan_id, true)))
157
+ return $this->admin->screen_scan_not_found(WPLNST_Admin::get_text('crawler_results'));
158
+
159
+ // Prepare scans data
160
+ $scans = (object) array(
161
+ 'isolated' => true,
162
+ 'rows' => array($scan),
163
+ );
164
+
165
+ // Check status param
166
+ $status_code = $status_level = false;
167
+ $status_test = isset($_GET['status'])? $_GET['status'] : false;
168
+ if (false !== $status_test) {
169
+ if (1 == strlen($status_test)) {
170
+ if ('0' == $status_test || in_array($status_test, $scan->status_levels))
171
+ $status_level = $status_test;
172
+ } elseif (3 == strlen($status_test)) {
173
+ $status_code = $status_test;
174
+ }
175
+ }
176
+
177
+ // Check object type param
178
+ $object_post_type = false;
179
+ $object_type = isset($_GET['otype'])? $_GET['otype'] : false;
180
+ if (false !== $object_type) {
181
+ if (!in_array($object_type, array_keys(WPLNST_Core_Types::get_objects_types()))) {
182
+ if (0 === strpos($object_type, 'posts_') && strlen($object_type) > 6) {
183
+ $object_post_type_test = mb_substr($object_type, 6);
184
+ if (in_array($object_post_type_test, $scan->post_types))
185
+ $object_post_type = $object_post_type_test;
186
+ }
187
+ $object_type = false;
188
+ }
189
+ }
190
+
191
+ // Check link type param
192
+ $link_type = isset($_GET['ltype'])? $_GET['ltype'] : false;
193
+ if (false !== $link_type && !in_array($link_type, array_keys(WPLNST_Core_Types::get_link_types())))
194
+ $link_type = false;
195
+
196
+ // Check ignored or not
197
+ $ignored_type = isset($_GET['ig'])? $_GET['ig'] : false;
198
+ if (false !== $ignored_type && !in_array($ignored_type, array_keys(WPLNST_Core_Types::get_ignored_types())))
199
+ $ignored_type = false;
200
+
201
+ // Check SEO links
202
+ $seo_link_type = isset($_GET['slt'])? $_GET['slt'] : false;
203
+ if (false !== $seo_link_type && !in_array($seo_link_type, array_keys(WPLNST_Core_Types::get_seo_link_types())))
204
+ $seo_link_type = false;
205
+
206
+ // Check protocol
207
+ $protocol_type = isset($_GET['pt'])? $_GET['pt'] : false;
208
+ if (false !== $protocol_type && !in_array($protocol_type, array_keys(WPLNST_Core_Types::get_protocol_types())))
209
+ $protocol_type = false;
210
+
211
+ // Check special
212
+ $special_type = isset($_GET['sp'])? $_GET['sp'] : false;
213
+ if (false !== $special_type && !in_array($special_type, array_keys(WPLNST_Core_Types::get_special_types())))
214
+ $special_type = false;
215
+
216
+ // Check action
217
+ $action_type = isset($_GET['ac'])? $_GET['ac'] : false;
218
+ if (false !== $action_type && !in_array($action_type, array_keys(WPLNST_Core_Types::get_action_types())))
219
+ $action_type = false;
220
+
221
+ // Check destination
222
+ $dest_type = isset($_GET['dtype'])? $_GET['dtype'] : false;
223
+ if (false !== $dest_type && !in_array($dest_type, array_keys(WPLNST_Core_Types::get_destination_types())))
224
+ $dest_type = false;
225
+
226
+ // Check order
227
+ $order_type = isset($_GET['or'])? $_GET['or'] : false;
228
+ if (false !== $order_type && !in_array($order_type, array_keys(WPLNST_Core_Types::get_crawl_order())) && !in_array($order_type, array_keys(WPLNST_Core_Types::get_order_types())))
229
+ $order_type = false;
230
+
231
+ // Check search URL
232
+ $search_url = $search_url_type = false;
233
+ $search_url_test = isset($_GET['surl'])? trim(stripslashes($_GET['surl'])) : false;
234
+ if (false !== $search_url_test && '' !== $search_url_test) {
235
+ $search_url = $search_url_test;
236
+ $search_url_type = isset($_GET['surlt'])? $_GET['surlt'] : false;
237
+ if (false !== $search_url_type && !in_array($search_url_type, array_keys(WPLNST_Core_Types::get_url_search_filters())))
238
+ $search_url_type = false;
239
+ }
240
+
241
+ // Check search anchor
242
+ $search_anchor = $search_anchor_type = false;
243
+ $search_anchor_test = isset($_GET['sanc'])? trim(stripslashes($_GET['sanc'])) : false;
244
+ if (false !== $search_anchor_test && '' !== $search_anchor_test) {
245
+ $search_anchor = $search_anchor_test;
246
+ $search_anchor_type = isset($_GET['sanct'])? $_GET['sanct'] : false;
247
+ if (false !== $search_anchor_type && !in_array($search_anchor_type, array_keys(WPLNST_Core_Types::get_anchor_search_filters())))
248
+ $search_anchor_type = false;
249
+ }
250
+
251
+ // Access scan data
252
+ $results = $this->admin->scans->get_scan_results(array(
253
+ 'scan_id' => $scan->id,
254
+ 'status_code' => $status_code,
255
+ 'status_level' => $status_level,
256
+ 'object_type' => $object_type,
257
+ 'object_post_type' => $object_post_type,
258
+ 'link_type' => $link_type,
259
+ 'ignored_type' => $ignored_type,
260
+ 'seo_link_type' => $seo_link_type,
261
+ 'protocol_type' => $protocol_type,
262
+ 'special_type' => $special_type,
263
+ 'action_type' => $action_type,
264
+ 'dest_type' => $dest_type,
265
+ 'order_type' => $order_type,
266
+ 'search_url' => $search_url,
267
+ 'search_url_type' => $search_url_type,
268
+ 'search_anchor' => $search_anchor,
269
+ 'search_anchor_type' => $search_anchor_type,
270
+ 'order_date' => $scan->crawl_order,
271
+ 'paged' => empty($_GET['paged'])? 0 : (int) $_GET['paged'],
272
+ ));
273
+
274
+ // Unknown error
275
+ if (false === $results)
276
+ return $this->admin->screen_unknown_error(WPLNST_Admin::get_text('crawler_results'));
277
+
278
+ // Define all results
279
+ $results->is_search = (false !== $object_type) || (false !== $object_post_type) || (false !== $link_type) || (false !== $ignored_type) || (false !== $seo_link_type) || (false !== $protocol_type) || (false !== $special_type) || (false !== $action_type) || (false !== $dest_type) || (false !== $search_url) || (false !== $search_anchor);
280
+ $results->is_all_results = (false === $status_code) && (false === $status_level) && !$results->is_search;
281
+
282
+ // Check total data
283
+ if ($results->total_rows > 0 && $results->is_all_results && (!isset($scan->summary['status_total']) || $scan->summary['status_total'] != $results->total_rows)) {
284
+
285
+ // Update total
286
+ $scan->summary['status_total'] = $results->total_rows;
287
+ $this->admin->scans->update_scan_summary($scan->id, array('status_total' => $scan->summary['status_total']));
288
+ }
289
+
290
+ // Results properties
291
+ $results->scan = $scan;
292
+ $results->isolated = false;
293
+
294
+ // Results filters
295
+ $results->status_code = $status_code;
296
+ $results->status_level = $status_level;
297
+ $results->object_type = $object_type;
298
+ $results->object_post_type = $object_post_type;
299
+ $results->link_type = $link_type;
300
+ $results->ignored_type = $ignored_type;
301
+ $results->seo_link_type = $seo_link_type;
302
+ $results->protocol_type = $protocol_type;
303
+ $results->special_type = $special_type;
304
+ $results->action_type = $action_type;
305
+ $results->destination_type = $dest_type;
306
+ $results->order_type = $order_type;
307
+ $results->search_url = $search_url;
308
+ $results->search_url_type = $search_url_type;
309
+ $results->search_anchor = $search_anchor;
310
+ $results->search_anchor_type = $search_anchor_type;
311
+
312
+ // Custom action view
313
+ add_action('wplnst_scans_results_view', array(&$this, 'scans_results_view'));
314
+
315
+ // Show admin screen
316
+ $this->admin->screen_view(array(
317
+ 'scans' => $scans,
318
+ 'results' => $results,
319
+ 'wp_action' => 'wplnst_scans_results_view',
320
+ 'title' => WPLNST_Admin::get_text('crawler_results'),
321
+ 'add_item_text' => WPLNST_Admin::get_text('scan_new_add'),
322
+ 'add_item_url' => WPLNST_Core_Plugin::get_url_scans_add(),
323
+ ));
324
+ }
325
+
326
+
327
+
328
+ /**
329
+ * Extension view to display the scans results list
330
+ */
331
+ public function scans_results_view($args) {
332
+
333
+ // Scan results row actions
334
+ add_filter('wplnst_results_actions_url', array(&$this, 'scans_results_actions_url'), 10, 2);
335
+ add_filter('wplnst_results_actions_status', array(&$this, 'scans_results_actions_status'), 10, 2);
336
+ add_filter('wplnst_results_actions_anchor', array(&$this, 'scans_results_actions_anchor'), 10, 2);
337
+
338
+ // Display an isolated scan
339
+ if (isset($args['scans']))
340
+ $this->scans_list_view(array('scans' => $args['scans']));
341
+
342
+ // Handle the results view callback
343
+ add_action('wplnst_scans_results_view_display', array(&$this, 'scans_results_view_display'));
344
+
345
+ // Results list table
346
+ $this->scans_results_views_table($args);
347
+ }
348
+
349
+
350
+
351
+ /**
352
+ * Show a list table for scan results
353
+ */
354
+ protected function scans_results_views_table($args) {
355
+ wplnst_require('views', 'scans-results');
356
+ $list = new WPLNST_Views_Scans_Results($args['results']);
357
+ $list->prepare_items();
358
+ $list->display();
359
+ }
360
+
361
+
362
+
363
+ /**
364
+ * Reserved for extra element before display results table
365
+ */
366
+ public function scans_results_view_display() {}
367
+
368
+
369
+
370
+ /**
371
+ * Define column URL row actions
372
+ */
373
+ public function scans_results_actions_url($actions, $item) {
374
+
375
+ // Check unlinked
376
+ if ($item['unlinked'])
377
+ return false;
378
+
379
+ // Check actions var
380
+ if (!isset($actions) || !is_array($actions))
381
+ $actions = array();
382
+
383
+ // More actions
384
+ $actions = apply_filters('wplnst_results_actions_url_extended', $actions, $item);
385
+
386
+ // Visit URL
387
+ $actions['wplnst-action-url-visit'] = '<a href="'.esc_url($item['url']).'" target="_blank">'.__('Visit', 'wplnst').'</a>';
388
+
389
+ // Done
390
+ return $actions;
391
+ }
392
+
393
+
394
+
395
+ /**
396
+ * Define column Status row actions
397
+ */
398
+ public function scans_results_actions_status($actions, $item) {
399
+
400
+ // Check unlinked
401
+ if ($item['unlinked'])
402
+ return false;
403
+
404
+ // Check actions var
405
+ if (!isset($actions) || !is_array($actions))
406
+ $actions = array();
407
+
408
+ // More actions
409
+ $actions = apply_filters('wplnst_results_actions_status_extended', $actions, $item);
410
+
411
+ // Done
412
+ return $actions;
413
+ }
414
+
415
+
416
+
417
+ /**
418
+ * Define column Anchor row actions
419
+ */
420
+ public function scans_results_actions_anchor($actions, $item) {
421
+
422
+ // Check unlinked
423
+ if ($item['unlinked'])
424
+ return false;
425
+
426
+ // Check actions var
427
+ if (!isset($actions) || !is_array($actions))
428
+ $actions = array();
429
+
430
+ // More actions
431
+ $actions = apply_filters('wplnst_results_actions_anchor_extended', $actions, $item);
432
+
433
+ // Done
434
+ return $actions;
435
+ }
436
+
437
+
438
+
439
+ // Scan crawler
440
+ // ---------------------------------------------------------------------------------------------------
441
+
442
+
443
+
444
+ /**
445
+ * Attemp to start or stop a crawler
446
+ */
447
+ private function scans_crawler($scan_id) {
448
+
449
+ // Atempt to load scan
450
+ if (false === ($scan = $this->admin->get_scan_by_id($scan_id, true)))
451
+ return $this->admin->screen_scan_not_found(WPLNST_Admin::get_text('crawler_action'));
452
+
453
+ // Check valid operation
454
+ $operation = empty($_GET['operation'])? false : (in_array($_GET['operation'], array('on', 'off'))? $_GET['operation'] : false);
455
+ if (false === $operation)
456
+ return $this->admin->screen_invalid_data(WPLNST_Admin::get_text('crawler_action'));
457
+
458
+ // Initialize
459
+ $notice_error = false;
460
+ $notice_warning = false;
461
+ $notice_success = false;
462
+
463
+ // Check nonce
464
+ if (empty($_GET['nonce']) || !wp_verify_nonce($_GET['nonce'], 'scan-crawler-'.$scan->hash)) {
465
+
466
+ // Not valid data
467
+ return $this->admin->screen_invalid_nonce(WPLNST_Admin::get_text('crawler_action'));
468
+
469
+ // Check if is playing right now
470
+ } elseif ('on' == $operation && 'play' == $scan->status) {
471
+
472
+ // Already started
473
+ $notice_warning = __('The crawling process for this scan is already started.', 'wplnst');
474
+
475
+ // Check if is stopped right now
476
+ } elseif ('off' == $operation && in_array($scan->status, array('stop', 'wait'))) {
477
+
478
+ // Already started
479
+ $notice_warning = __('The crawling process for this scan is already stopped.', 'wplnst');
480
+
481
+ // Avoid ended scans
482
+ } elseif ('end' == $scan->status) {
483
+
484
+ // Not available scan
485
+ $notice_warning = __('This scan was completed and it is not possible to start again.', 'wplnst');
486
+
487
+ // Check submit form
488
+ } else {
489
+
490
+ // Stop current scan
491
+ if ('off' == $operation) {
492
+
493
+ // Remove queued flag if never started
494
+ if ('queued' == $scan->status && (empty($scan->row->started_at) || '0000-00-00 00:00:00' == $scan->row->started_at)) {
495
+
496
+ // Start current scan
497
+ if (!$this->admin->scans->unqueue_scan($scan->id)) {
498
+
499
+ // Something failed
500
+ $notice_error = __('Something went wrong and the unqueue process was failed.', 'wplnst');
501
+
502
+ // Done
503
+ } else {
504
+
505
+ // Updated
506
+ $notice_success = __('The crawler for this scan is back to the wait mode.', 'wplnst');
507
+ }
508
+
509
+ // Normal mode
510
+ } else {
511
+
512
+ // Start current scan
513
+ if (!$this->admin->scans->stop_scan($scan->id)) {
514
+
515
+ // Something failed
516
+ $notice_error = __('Something went wront and the crawler stop was failed.', 'wplnst');
517
+
518
+ // Done
519
+ } else {
520
+
521
+ // Updated
522
+ $notice_success = sprintf(__('The crawler for this scan is stopped. You can see its collected data in the <a href="%s">crawler results page</a>.', 'wplnst'), esc_url(WPLNST_Core_Plugin::get_url_scans_results($scan_id)));
523
+ }
524
+ }
525
+
526
+ // Run when cURL enabled
527
+ } elseif (wplnst_is_curl_enabled()) {
528
+
529
+ // Check max scans running
530
+ if (!$this->admin->scans->can_play_more_scans()) {
531
+
532
+ // Max scans allowed
533
+ $notice_error = WPLNST_Admin::get_text('max_scans');
534
+
535
+ // Queue scan
536
+ $this->admin->scans->queue_scan($scan->id);
537
+
538
+ // Check scan ready
539
+ } elseif (!$scan->ready) {
540
+
541
+ // Something failed
542
+ $notice_error = sprintf(__('You need to complete some critical values before start the crawler, please <a href="%s">edit this scan</a>.', 'wplnst'), WPLNST_Core_Plugin::get_url_scans_edit($scan->id));
543
+
544
+ // Start attempt
545
+ } else {
546
+
547
+ // Not continued
548
+ $continued = false;
549
+
550
+ // Register stopped time
551
+ if ('stop' == $scan->status || 'queued' == $scan->status) {
552
+
553
+ // Needed to be started before
554
+ if (empty($scan->row->started_at) || '0000-00-00 00:00:00' == $scan->row->started_at) {
555
+
556
+ // Remove stopped time
557
+ $this->admin->scans->remove_stopped_time($scan->id);
558
+ $this->admin->scans->update_scan_summary($scan->id, array('time_stopped' => 0));
559
+
560
+ // Check stopped time
561
+ } elseif (!empty($scan->row->stopped_at) && '0000-00-00 00:00:00' != $scan->row->stopped_at) {
562
+
563
+ // Continued
564
+ $continued = true;
565
+
566
+ // Calculate stopped time
567
+ $time_stopped = time() - strtotime($scan->row->stopped_at.' UTC');
568
+ $summary = $this->admin->scans->get_scan_summary($scan->id);
569
+ $time_stopped += isset($summary['time_stopped'])? (int) $summary['time_stopped'] : 0;
570
+ }
571
+ }
572
+
573
+ // Start current scan
574
+ if (!$this->admin->scans->play_scan($scan->id, $continued)) {
575
+
576
+ // Something failed
577
+ $notice_error = __('Something went wront and the crawler start was failed.', 'wplnst');
578
+
579
+ // Done
580
+ } else {
581
+
582
+ // Check salt file
583
+ if (empty($notice_warning) && !WPLNST_Core_Nonce::check_salt_file())
584
+ $notice_warning = WPLNST_Admin::get_text('no_salt');
585
+
586
+ // Update stopped time
587
+ if (isset($time_stopped))
588
+ $this->admin->scans->update_scan_summary($scan->id, array('time_stopped' => $time_stopped));
589
+
590
+ // Save default threads values
591
+ $this->admin->scans->set_scan_final_threads_options($scan->id, array(
592
+ 'max_threads' => $scan->threads->max,
593
+ 'connect_timeout' => $scan->threads->connect_timeout,
594
+ 'request_timeout' => $scan->threads->request_timeout,
595
+ ));
596
+
597
+ // Start process now if are not running scans
598
+ $this->scans_crawler_run($scan->id, $scan->hash);
599
+
600
+ // Updated
601
+ $notice_success = sprintf(__('The crawler is running, you can see its data in the <a href="%s">crawler results page</a>.', 'wplnst'), esc_url(WPLNST_Core_Plugin::get_url_scans_results($scan_id)));
602
+ }
603
+ }
604
+ }
605
+ }
606
+
607
+ // Reload scan
608
+ if (false === ($scan = $this->admin->get_scan_by_id($scan_id, true, true)))
609
+ return $this->admin->screen_scan_not_found(WPLNST_Admin::get_text('crawler_action'));
610
+
611
+ // Prepare scans data
612
+ $scans = (object) array(
613
+ 'isolated' => true,
614
+ 'rows' => array($scan),
615
+ );
616
+
617
+ // Custom action view
618
+ add_action('wplnst_scans_crawler_view', array(&$this, 'scans_list_view'));
619
+
620
+ // Screen showing the start crawling process
621
+ $this->admin->screen_view(array(
622
+ 'scans' => $scans,
623
+ 'title' => WPLNST_Admin::get_text('crawler_action'),
624
+ 'notice_error' => $notice_error,
625
+ 'notice_warning' => $notice_warning,
626
+ 'notice_success' => $notice_success,
627
+ 'wp_action' => 'wplnst_scans_crawler_view',
628
+ 'add_item_text' => WPLNST_Admin::get_text('scan_new_add'),
629
+ 'add_item_url' => WPLNST_Core_Plugin::get_url_scans_add(),
630
+ ));
631
+
632
+ // Check for queued scans
633
+ WPLNST_Core_Alive::activity(true);
634
+ }
635
+
636
+
637
+
638
+ /**
639
+ * Wrapper function to run scan from Alive class
640
+ */
641
+ protected function scans_crawler_run($scan_id, $hash) {
642
+ WPLNST_Core_Alive::run($scan_id, $hash);
643
+ }
644
+
645
+
646
+
647
+ // Scan delete
648
+ // ---------------------------------------------------------------------------------------------------
649
+
650
+
651
+
652
+ /**
653
+ * Attemp to remove a scan
654
+ */
655
+ private function scans_delete($scan_id) {
656
+
657
+ // Initialize
658
+ $notice_success = false;
659
+ $notice_warning = false;
660
+
661
+ // Check multiple scans
662
+ if (isset($_GET['scan_id']) && '-' == mb_substr($_GET['scan_id'], 0, 1)) {
663
+
664
+ // Check nonce
665
+ if (empty($_GET['nonce']) || !wp_verify_nonce($_GET['nonce'], 'bulk-scans-delete'))
666
+ return $this->admin->screen_invalid_nonce(WPLNST_Admin::get_text('scan_delete'));
667
+
668
+ // Check confirmation
669
+ if (empty($_GET['confirm']) || 'on' != $_GET['confirm']) {
670
+
671
+ // Confirm message
672
+ $notice_warning = sprintf(__('Sorry, we need a confirmation action. Please click here to <a href="%s" class="wplnst-scan-delete" data-confirm="%s">delete scan</a>', 'wplnst'), esc_url(WPLNST_Core_Plugin::get_url_scans_delete($_GET['scan_id'], 'bulk-scans-delete')), esc_attr(WPLNST_Admin::get_text('scan_delete_confirm')));
673
+
674
+ // Done
675
+ } else {
676
+
677
+ // Timeouts
678
+ set_time_limit(0);
679
+
680
+ // Initialize
681
+ $scans = array();
682
+
683
+ // Extract identifiers
684
+ $ids = array_map('intval', explode('-', trim($_GET['scan_id'], '-')));
685
+ foreach ($ids as $scan_id) {
686
+ if (!empty($scan_id) && false !== ($scan = $this->admin->get_scan_by_id($scan_id)))
687
+ $scans[] = $scan_id;
688
+ }
689
+
690
+ // Check data
691
+ if (empty($scans))
692
+ return $this->admin->screen_invalid_data(WPLNST_Admin::get_text('scan_delete'));
693
+
694
+ // Enum and remove
695
+ foreach ($scans as $scan_id)
696
+ $this->admin->scans->delete_scan($scan_id);
697
+
698
+ // Success message
699
+ $notice_success = __('The scans have been removed.', 'wplnst');
700
+ }
701
+
702
+ // Single
703
+ } else {
704
+
705
+ // Atempt to load scan
706
+ if (false === ($scan = $this->admin->get_scan_by_id($scan_id)))
707
+ return $this->admin->screen_scan_not_found(WPLNST_Admin::get_text('scan_delete'));
708
+
709
+ // Check nonce
710
+ if (empty($_GET['nonce']) || !wp_verify_nonce($_GET['nonce'], $scan->hash))
711
+ return $this->admin->screen_invalid_nonce(WPLNST_Admin::get_text('scan_delete'));
712
+
713
+ // Check confirmation
714
+ if (empty($_GET['confirm']) || 'on' != $_GET['confirm']) {
715
+
716
+ // Confirm message
717
+ $notice_warning = sprintf(__('Sorry, we need a confirmation action. Please click here to <a href="%s" class="wplnst-scan-delete-isolated" data-confirm-delete="%s">delete scan</a>', 'wplnst'), esc_url(WPLNST_Core_Plugin::get_url_scans_delete($scan->id, $scan->hash)), esc_attr(WPLNST_Admin::get_text('scan_delete_confirm')));
718
+
719
+ // Done
720
+ } else {
721
+
722
+ // Remove scan
723
+ $this->admin->scans->delete_scan($scan_id);
724
+
725
+ // Success message
726
+ $notice_success = __('The scan has been removed.', 'wplnst');
727
+ }
728
+ }
729
+
730
+ // Custom action view
731
+ add_action('wplnst_scans_delete_view', array(&$this, 'scans_delete_view'));
732
+
733
+ // Show admin screen
734
+ $this->admin->screen_view(array(
735
+ 'title' => WPLNST_Admin::get_text('scan_delete'),
736
+ 'notice_success' => $notice_success,
737
+ 'notice_warning' => $notice_warning,
738
+ 'wp_action' => 'wplnst_scans_delete_view',
739
+ 'add_item_text' => WPLNST_Admin::get_text('scan_new_add'),
740
+ 'add_item_url' => WPLNST_Core_Plugin::get_url_scans_add(),
741
+ ));
742
+
743
+ // Check for queued scans
744
+ WPLNST_Core_Alive::activity(true);
745
+ }
746
+
747
+
748
+
749
+ /**
750
+ * Extension view when scan deleted
751
+ */
752
+ public function scans_delete_view($args) {
753
+
754
+ // Back to the scans screen
755
+ echo '<p>&nbsp;&nbsp; &laquo; <a href="'.WPLNST_Core_Plugin::get_url_scans().'">'.__('Back to the scans list', 'wplnst').'</a></p>';
756
+ }
757
+
758
+
759
+
760
+ // New scan or edit
761
+ // ---------------------------------------------------------------------------------------------------
762
+
763
+
764
+
765
+ /**
766
+ * New or edit scan
767
+ */
768
+ private function scans_edit($scan_id = 0) {
769
+
770
+ // Scans library
771
+ $this->admin->load_scans_object();
772
+
773
+ // Notice initialization
774
+ $notice_error = isset($this->admin->scan_submit)? $this->admin->scan_submit->notice_error : false;
775
+ $notice_warning = isset($this->admin->scan_submit)? $this->admin->scan_submit->notice_warning : false;
776
+ $notice_success = isset($this->admin->scan_submit)? $this->admin->scan_submit->notice_success : false;
777
+ $notice_crawler = isset($this->admin->scan_submit)? $this->admin->scan_submit->notice_crawler : false;
778
+
779
+ // Check submit error
780
+ if ($notice_error) {
781
+ return $this->admin->screen_view(array(
782
+ 'title' => WPLNST_Admin::get_text('scan_edit'),
783
+ 'notice_error' => $notice_error,
784
+ ));
785
+ }
786
+
787
+ // Check existing scan
788
+ if (!empty($scan_id)) {
789
+
790
+ // Atempt to load scan
791
+ if (false === ($scan = $this->admin->get_scan_by_id($scan_id, true)))
792
+ return $this->admin->screen_scan_not_found(WPLNST_Admin::get_text('scan_edit'));
793
+
794
+ // Check update
795
+ if (!$notice_success && !empty($_GET['updated']) && 'on' == $_GET['updated']) {
796
+
797
+ // New scan created
798
+ $notice_success = __('New scan added successfully.', 'wplnst');
799
+
800
+ // Check started argument
801
+ if (!empty($_GET['started'])) {
802
+
803
+ // Check max scans
804
+ if ('max_scans' == $_GET['started']) {
805
+
806
+ // Warning message
807
+ $notice_warning = WPLNST_Admin::get_text('max_scans');
808
+
809
+ // Check error
810
+ } elseif ('error' == $_GET['started']) {
811
+
812
+ // Warning message
813
+ $notice_warning = sprintf(__('Something went wrong trying to start the crawler for this new scan.'));
814
+
815
+ // Scan running
816
+ } elseif ('on' == $_GET['started']) {
817
+
818
+ // New crawler running
819
+ $notice_crawler = sprintf(__('The crawler for this new scan is running. You can see its data in the <a href="%s">crawler results page</a>.', 'wplnst'), esc_url(WPLNST_Core_Plugin::get_url_scans_results($scan_id)));
820
+ }
821
+
822
+ // Not started
823
+ } elseif ('wait' == $scan->status && $this->admin->scans->can_play_more_scans() && true === $this->admin->scans->is_scan_ready($scan)) {
824
+
825
+ // Invite to start the crawler
826
+ $start_url = esc_url(WPLNST_Core_Plugin::get_url_scans_crawler($scan->id, 'on', $scan->hash));
827
+ $notice_success .= ' '.sprintf(__('From now on you can <a href="%s">start the crawler</a>.', 'wplnst'), $start_url);
828
+ }
829
+ }
830
+
831
+ // Check salt file
832
+ if ('play' == $scan->status && empty($notice_warning) && !WPLNST_Core_Nonce::check_salt_file())
833
+ $notice_warning = WPLNST_Admin::get_text('no_salt');
834
+ }
835
+
836
+ // Default values
837
+ if (empty($scan)) {
838
+
839
+ // Prepare scan object
840
+ $scan = (object) array(
841
+ 'id' => 0,
842
+ 'name' => '',
843
+ 'status' => 'wait',
844
+ 'destination_type' => 'all',
845
+ 'time_scope' => 'anytime',
846
+ 'link_types' => array('links', 'images'),
847
+ 'crawl_order' => 'desc',
848
+ 'redir_status' => true,
849
+ 'malformed' => true,
850
+ 'notify_default' => true,
851
+ 'notify_address' => false,
852
+ 'notify_address_email' => '',
853
+ 'post_types' => array('post', 'page'),
854
+ 'post_status' => array('publish'),
855
+ 'comment_types' => array(),
856
+ 'check_comments' => false,
857
+ 'check_blogroll' => false,
858
+ 'status_levels' => array('2', '3', '4', '5'),
859
+ 'status_codes' => array(),
860
+ 'custom_fields' => array(),
861
+ 'anchor_filters' => array(),
862
+ 'include_urls' => array(),
863
+ 'exclude_urls' => array(),
864
+ 'html_attributes' => array(),
865
+ 'filtered_query' => true,
866
+ 'threads' => 0,
867
+ 'connect_timeout' => 0,
868
+ 'request_timeout' => 0,
869
+ );
870
+
871
+ // Check scan
872
+ } else {
873
+
874
+ // Analyze data
875
+ $ready = $this->admin->scans->is_scan_ready($scan);
876
+ $is_ready = (true === $ready);
877
+
878
+ // Prepare notifications
879
+ if (!$is_ready)
880
+ $notice_ready = $ready;
881
+
882
+ // Check ready update
883
+ if ($scan->ready != $is_ready)
884
+ $this->admin->scans->update_scan_ready($scan->id, $is_ready);
885
+ }
886
+
887
+ // Custom action view
888
+ add_action('wplnst_scans_edit_view', array(&$this, 'scans_edit_view'));
889
+
890
+ // Display page
891
+ $this->admin->screen_view(array(
892
+ 'scan' => $scan,
893
+ 'wp_action' => 'wplnst_scans_edit_view',
894
+ 'destination_types' => WPLNST_Core_Types::get_destination_types(),
895
+ 'time_scopes' => WPLNST_Core_Types::get_time_scopes(),
896
+ 'link_types' => WPLNST_Core_Types::get_link_types(),
897
+ 'crawl_order' => WPLNST_Core_Types::get_crawl_order(),
898
+ 'custom_fields' => WPLNST_Core_Types::get_custom_fields(),
899
+ 'anchor_filters' => WPLNST_Core_Types::get_anchor_filters(),
900
+ 'url_filters' => WPLNST_Core_Types::get_url_filters(),
901
+ 'html_attributes_having' => WPLNST_Core_Types::get_html_attributes_having(),
902
+ 'html_attributes_operators' => WPLNST_Core_Types::get_html_attributes_operators(),
903
+ 'post_types' => WPLNST_Core_Types::get_post_types(),
904
+ 'post_status' => WPLNST_Core_Types::get_post_status(),
905
+ 'comment_types' => WPLNST_Core_Types::get_comment_types(),
906
+ 'status_levels' => WPLNST_Core_Types::get_status_levels(),
907
+ 'status_codes' => WPLNST_Core_Types::get_status_codes(),
908
+ 'nonce' => wp_create_nonce('scan-edit-'.(empty($scan->id)? '0' : $scan->hash)),
909
+ 'action' => ($scan->id > 0)? WPLNST_Core_Plugin::get_url_scans_edit($scan->id) : WPLNST_Core_Plugin::get_url_scans_add(),
910
+ 'title' => ($scan->id > 0)? WPLNST_Admin::get_text('scan_edit') : WPLNST_Admin::get_text('scan_new'),
911
+ 'more_scans' => $this->admin->scans->can_play_more_scans(),
912
+ 'notice_success' => $notice_success,
913
+ 'notice_crawler' => $notice_crawler,
914
+ 'notice_warning' => $notice_warning,
915
+ 'notice_ready' => isset($notice_ready)? $notice_ready : false,
916
+ 'default_max_threads' => wplnst_get_nsetting('max_threads'),
917
+ 'default_connect_timeout' => wplnst_get_nsetting('connect_timeout'),
918
+ 'default_request_timeout' => wplnst_get_nsetting('request_timeout'),
919
+ 'add_item_text' => ($scan->id > 0 && 'wait' != $scan->status)? WPLNST_Admin::get_text('scan_new_add') : '',
920
+ 'add_item_url' => ($scan->id > 0 && 'wait' != $scan->status)? WPLNST_Core_Plugin::get_url_scans_add() : '',
921
+ ));
922
+ }
923
+
924
+
925
+
926
+ /**
927
+ * Extension view for the edit scan screen
928
+ */
929
+ public function scans_edit_view($args) {
930
+ wplnst_require('views', 'scans-edit');
931
+ WPLNST_Views_Scans_Edit::view($args);
932
+ }
933
+
934
+
935
+
936
+ }
admin/settings.php ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * WP Link Status Admin Settings class
5
+ *
6
+ * @package WP Link Status
7
+ * @subpackage WP Link Status Admin
8
+ */
9
+ class WPLNST_Admin_Settings {
10
+
11
+
12
+
13
+ /**
14
+ * Constructor
15
+ */
16
+ public function __construct(&$admin) {
17
+
18
+ // Initialize
19
+ $notice_success = $notice_error = false;
20
+
21
+ // Check submit
22
+ if (isset($_POST['settings_nonce'])) {
23
+
24
+ // Check nonce
25
+ if (!wp_verify_nonce($_POST['settings_nonce'], WPLNST_Core_Plugin::slug.'-settings'))
26
+ return $admin->screen_invalid_nonce(WPLNST_Admin::get_text('settings'));
27
+
28
+ // General update
29
+ update_option('wplnst_max_threads', (int) $_POST['tx-max-threads']);
30
+ update_option('wplnst_max_scans', (int) $_POST['tx-max-scans']);
31
+ update_option('wplnst_max_pack', (int) $_POST['tx-max-pack']);
32
+ update_option('wplnst_max_requests', (int) $_POST['tx-max-requests']);
33
+ update_option('wplnst_max_redirs', (int) $_POST['tx-max-redirs']);
34
+ update_option('wplnst_max_download', (int) $_POST['tx-max-download']);
35
+ update_option('wplnst_user_agent', stripslashes($_POST['tx-user-agent']));
36
+
37
+ // Timeouts update
38
+ update_option('wplnst_connect_timeout', (int) $_POST['tx-connect-timeout']);
39
+ update_option('wplnst_request_timeout', (int) $_POST['tx-request-timeout']);
40
+ update_option('wplnst_extra_timeout', (int) $_POST['tx-extra-timeout']);
41
+ update_option('wplnst_crawler_alive', (int) $_POST['tx-crawler-alive']);
42
+ update_option('wplnst_total_objects', (int) $_POST['tx-total-objects']);
43
+ update_option('wplnst_summary_status', (int) $_POST['tx-summary-status']);
44
+ update_option('wplnst_summary_phases', (int) $_POST['tx-summary-phases']);
45
+ update_option('wplnst_summary_objects', (int) $_POST['tx-summary-objects']);
46
+
47
+ // Advanced update
48
+ update_option('wplnst_recursion_limit', (int) $_POST['tx-recursion-limit']);
49
+ update_option('wplnst_mysql_calc_rows', empty($_POST['ck-mysql-calc-rows'])? 'off' : 'on');
50
+ update_option('wplnst_uninstall_data', empty($_POST['ck-uninstall-data'])? 'off' : 'on');
51
+
52
+ // Update notice
53
+ $notice_success = __('Settings updated', 'wplnst');
54
+ }
55
+
56
+ // Custom action view
57
+ add_action('wplnst_view_settings', array(&$this, 'view_settings'));
58
+
59
+ // Show settings screen
60
+ $admin->screen_view(array(
61
+ 'title' => WPLNST_Admin::get_text('settings'),
62
+ 'wp_action' => 'wplnst_view_settings',
63
+ 'action' => WPLNST_Core_Plugin::get_url_settings(),
64
+ 'nonce' => wp_create_nonce(WPLNST_Core_Plugin::slug.'-settings'),
65
+ 'notice_success' => $notice_success,
66
+ 'notice_error' => $notice_error,
67
+ ));
68
+ }
69
+
70
+
71
+
72
+ /**
73
+ * Extension view for settings page
74
+ */
75
+ public function view_settings($args) {
76
+ wplnst_require('views', 'settings');
77
+ WPLNST_Views_Settings::view($args);
78
+ }
79
+
80
+
81
+
82
+ }
assets/css/admin.css ADDED
@@ -0,0 +1,562 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ /* Tabs */
3
+
4
+ #wplnst-title {
5
+ margin-bottom: 5px;
6
+ }
7
+
8
+ #wplnst-tabs {
9
+ padding: 5px 0 0 15px;
10
+ }
11
+
12
+ .wplnst-tab {
13
+ display: none;
14
+ }
15
+
16
+ #wplnst-tabs.wplnst-tabs-scan-edit .wplnst-tab .form-table th {
17
+ width: 150px;
18
+ }
19
+
20
+ .wplnst-tab .form-table td {
21
+ vertical-align: top;
22
+ }
23
+
24
+ .wplnst-tab .form-table td.wplnst-list {
25
+ line-height: 1.9em;
26
+ }
27
+
28
+ .wplnst-tab .form-table td.wplnst-value {
29
+ padding-top: 20px;
30
+ line-height: 1.3em;
31
+ }
32
+
33
+ .wplnst-tab .form-table td.wplnst-value-list {
34
+ padding-top: 15px;
35
+ line-height: 1.9em;
36
+ }
37
+
38
+ .wplnst-tab-active {
39
+ display: block;
40
+ }
41
+
42
+ #wplnst-destination-type, #wplnst-time-scope, #wplnst-crawl-order {
43
+ width: 185px;
44
+ }
45
+
46
+ #wplnst-cf-new {
47
+ width: 215px;
48
+ }
49
+
50
+ a.wplnst-trash-editor, a.wplnst-trash-editor:hover {
51
+ color: #a00;
52
+ }
53
+
54
+ .wplnst-elist {
55
+ border: 0; display: none;
56
+ margin: 0 0 5px; padding: 0;
57
+ }
58
+
59
+ .wplnst-elist-visible { display: block; }
60
+ .wplnst-elist-readonly { margin: 0; }
61
+
62
+ .wplnst-elist tr {
63
+ margin-bottom: 10px;
64
+ }
65
+
66
+ .wplnst-elist tr td {
67
+ margin: 0; padding: 4px 5px;
68
+ }
69
+
70
+ .wplnst-elist tr td.wplnst-elist-val {
71
+ color: #5c5a5a;
72
+ background: #f9f9f9;
73
+ }
74
+
75
+ #wplnst-af-new { width: 361px; }
76
+ #wplnst-ius-new { width: 425px; }
77
+ #wplnst-eus-new { width: 425px; }
78
+
79
+ .wplnst-elist tr td.wplnst-cfs-val { width: 205px; }
80
+ .wplnst-elist tr td.wplnst-afs-val { width: 350px; }
81
+ .wplnst-elist tr td.wplnst-ius-val { width: 415px; }
82
+ .wplnst-elist tr td.wplnst-eus-val { width: 415px; }
83
+
84
+ .wplnst-elist tr td.wplnst-elist-type {
85
+ color: #6f6f6f;
86
+ }
87
+
88
+ .wplnst-elist tr td.wplnst-cfs-type { width: 53px; padding-left: 12px; }
89
+ .wplnst-elist tr td.wplnst-afs-type { width: 200px; padding-left: 0; }
90
+ .wplnst-elist tr td.wplnst-ius-type { width: 121px; padding-left: 14px; }
91
+ .wplnst-elist tr td.wplnst-eus-type { width: 121px; padding-left: 14px; }
92
+
93
+ .wplnst-elist tr td.wplnst-hes-ele { width: 55px; font-weight: bold; }
94
+ .wplnst-elist tr td.wplnst-hes-have { width: 87px; font-weight: bold; }
95
+ .wplnst-elist tr td.wplnst-hes-att { width: 125px; }
96
+ .wplnst-elist tr td.wplnst-hes-op { width: 112px; padding-left: 15px; font-weight: bold; }
97
+ .wplnst-elist tr td.wplnst-hes-val { width: 126px; }
98
+
99
+ .wplnst-elist tr td.wplnst-elist-close {
100
+ padding-left: 21px;
101
+ }
102
+
103
+ /* .wplnst-elist tr td.wplnst-afs-close { padding-left: 20px; } */
104
+
105
+ .wplnst-elist tr td.wplnst-elist-close .wplnst-elist-close-link {
106
+ display: block;
107
+ width: 16px; height: 16px;
108
+ outline: none; border: 0;
109
+ text-indent: -9999px;
110
+ background: url(../images/button-close.png) left top no-repeat;
111
+ }
112
+
113
+ .wplnst-elist tr td.wplnst-elist-close .wplnst-elist-close-link:hover {
114
+ background: url(../images/button-close-hover.png) left top no-repeat;
115
+ }
116
+
117
+ .wplnst-elist tr td.wplnst-elist-split {
118
+ height: 3px;
119
+ }
120
+
121
+
122
+
123
+ /* Scans */
124
+
125
+ strong.wplnst-scan-name {
126
+ display: block;
127
+ margin-bottom: 10px;
128
+ line-height: 1.7em;
129
+ }
130
+
131
+ strong.wplnst-scan-name img {
132
+ vertical-align: top;
133
+ }
134
+
135
+ .wplnst-scan-status {
136
+ font-size: x-small;
137
+ font-family: Arial;
138
+ font-weight: normal;
139
+ vertical-align: top;
140
+ text-transform: uppercase;
141
+ font-variant: small-caps;
142
+ margin: 0 2px 0 0; padding: 3px 4px 2px;
143
+ -moz-border-radius: 3px;
144
+ -webkit-border-radius: 3px;
145
+ border-radius: 3px;
146
+ -khtml-border-radius: 3px;
147
+ }
148
+
149
+ .wplnst-scan-status-play {
150
+ color: #fff;
151
+ background: #fa7900;
152
+ }
153
+
154
+ .wplnst-scan-status-queued {
155
+ color: #fff;
156
+ background: #7b7d7b;
157
+ }
158
+
159
+ .wplnst-scan-status-stop {
160
+ color: #fff;
161
+ background: #383838;
162
+ }
163
+
164
+ .wplnst-scan-status-wait {
165
+ color: #fff;
166
+ background: grey;
167
+ }
168
+
169
+ .wplnst-scan-status-end {
170
+ color: #fff;
171
+ background: #5e9b1c;
172
+ }
173
+
174
+ .wplnst-scan-status-line {
175
+ margin: 0 0 10px 1px;
176
+ }
177
+
178
+ .wplnst-scan-object-info {
179
+ color: #848585;
180
+ background: #e5e5e5;
181
+ margin: 0 4px 0 0;
182
+ padding: 3px 4px 3px 5px;
183
+ -moz-border-radius: 2px;
184
+ -webkit-border-radius: 2px;
185
+ border-radius: 2px;
186
+ -khtml-border-radius: 2px;
187
+ }
188
+
189
+ .wplnst-scan-object-running {
190
+ color: #56524e;
191
+ }
192
+
193
+ .wplnst-scan-object-wait {
194
+ background: #f2f2f2;
195
+ }
196
+
197
+ .wplnst-scan-object-completed {
198
+ color: #32373c;
199
+ }
200
+
201
+ .wplnst-scan-object-completed-end {
202
+ color: #5e6455;
203
+ }
204
+
205
+ .wplnst-scan-name-warning img {
206
+ padding-top: 2px;
207
+ }
208
+
209
+ .wplnst-scan-ready-info {
210
+ margin: 0 0 7px 1px;
211
+ padding: 0 0 0 23px;
212
+ background: url(../images/scan-launch.png) left 1px no-repeat;
213
+ }
214
+
215
+ .wplnst-scan-ready-info a {
216
+ font-weight: bold;
217
+ }
218
+
219
+ .wplnst-scan-time-info {
220
+ margin: 0 0 7px 1px;
221
+ padding: 0 0 0 23px;
222
+ background: url(../images/scan-clock.png) left 2px no-repeat;
223
+ }
224
+
225
+ .wplnst-scan-links-info {
226
+ margin: 0 0 7px 1px;
227
+ padding: 0 0 0 23px;
228
+ background: url(../images/scan-link.png) left 1px no-repeat;
229
+ }
230
+
231
+ .wplnst-scan-links-info strong {
232
+ color: #4b4b4b;
233
+ }
234
+
235
+ .wplnst-scan-time-info, .wplnst-scan-links-info {
236
+ color: #6f6f6f;
237
+ }
238
+
239
+ .wplnst-split-char {
240
+ color: #a5a5a5;
241
+ margin: 0 5px;
242
+ }
243
+
244
+ .wplnst-isolated-table {
245
+ margin-bottom: 8px;
246
+ }
247
+
248
+ .wplnst-col-input {
249
+ width: 40px;
250
+ }
251
+
252
+ .wplnst-col-info, .form-table td.wplnst-col-info {
253
+ padding: 17px 0 5px;
254
+ line-height: 1.7em;
255
+ }
256
+
257
+
258
+
259
+ /* Columns scan */
260
+
261
+ .column-wplnst-scans-name {
262
+ width: 40%;
263
+ }
264
+
265
+ .widefat td.column-wplnst-scans-name { padding-bottom: 15px; }
266
+ .widefat.wplnst-isolated-table td.column-wplnst-scans-name { padding-bottom: 6px; }
267
+
268
+ .column-wplnst-scans-configuration table, .widefat td.column-wplnst-scans-configuration table {
269
+ border-spacing: 0;
270
+ border-collapse: collapse;
271
+ margin: 0; padding: 0;
272
+ }
273
+
274
+ .column-wplnst-scans-configuration table td, .widefat td.column-wplnst-scans-configuration table td {
275
+ margin: 0; padding: 0 0 8px 0;
276
+ }
277
+
278
+ .column-wplnst-scans-configuration table td.wplnst-scans-configuration-row, .widefat td.column-wplnst-scans-configuration table td.wplnst-scans-configuration-row {
279
+ width: 100px;
280
+ }
281
+
282
+ .column-wplnst-scans-configuration table td strong, .widefat td.column-wplnst-scans-configuration table td strong {
283
+ color: #677476;
284
+ font-weight: bold;
285
+ }
286
+
287
+
288
+
289
+ /* Columns scan results */
290
+
291
+ .column-wplnst-url {
292
+ width: 33%;
293
+ }
294
+
295
+ .wplnst-row-url {
296
+ margin-bottom: 3px;
297
+ }
298
+
299
+ .wplnst-results-url-error-code {
300
+ color: #737373;
301
+ }
302
+
303
+ .wplnst-results-url-full {
304
+ color: #999;
305
+ }
306
+
307
+ .column-wplnst-status {
308
+ width: 21%;
309
+ }
310
+
311
+ .wplnst-url-status-code {
312
+ margin-bottom: 2px;
313
+ }
314
+
315
+ .wplnst-url-status-code-result {
316
+ font-weight: bold;
317
+ }
318
+
319
+ .wplnst-url-status-code-0 {
320
+ color: black;
321
+ }
322
+
323
+ .wplnst-url-status-code-2 {
324
+ color: green;
325
+ }
326
+
327
+ .wplnst-url-status-code-3 {
328
+ color: #ff8c00;
329
+ }
330
+
331
+ .wplnst-url-status-code-4 {
332
+ color: red;
333
+ }
334
+
335
+ .wplnst-url-status-code-redir {
336
+ font-weight: bold;
337
+ }
338
+
339
+ .wplnst-url-status-code-redir-arrow {
340
+ font-weight: normal;
341
+ }
342
+
343
+ .wplnst-url-status-info {
344
+ color: #7a7a7a;
345
+ margin-bottom: 2px;
346
+ }
347
+
348
+ .wplnst-url-status-info-split {
349
+ color: #ccc;
350
+ vertical-align: top;
351
+ }
352
+
353
+ .column-wplnst-anchor {
354
+ width: 24%;
355
+ }
356
+
357
+ .wplnst-anchor-image:before {
358
+ content: "\f128";
359
+ font-size: 1.1em;
360
+ }
361
+
362
+ .column-wplnst-content {
363
+ width: 22%;
364
+ }
365
+
366
+ .wplnst-row-dashicon {
367
+ margin-bottom: 5px;
368
+ }
369
+
370
+ .wplnst-row-dashicon span {
371
+ padding-left: 2px;
372
+ }
373
+
374
+ .wplnst-row-dashicon:before {
375
+ font-family: "dashicons";
376
+ padding-right: 3px;
377
+ vertical-align: middle;
378
+ color: #849699;
379
+ }
380
+
381
+ .wplnst-content-post:before {
382
+ content: "\f109";
383
+ }
384
+
385
+ .wplnst-content-comment:before {
386
+ content: "\f101";
387
+ }
388
+
389
+ .wplnst-content-bookmark:before {
390
+ content: "\f103";
391
+ }
392
+
393
+
394
+
395
+ /* Results mark */
396
+
397
+ .wplnst-results-mark {
398
+ font-size: xx-small;
399
+ font-family: Arial;
400
+ font-weight: normal !important;
401
+ vertical-align: top;
402
+ text-transform: uppercase;
403
+ font-variant: small-caps;
404
+ color: #fff;
405
+ margin: 0; padding: 1px 2px;
406
+ border: 1px solid #fff;
407
+ -webkit-border-radius: 3px;
408
+ -moz-border-radius: 3px;
409
+ border-radius: 3px;
410
+ -khtml-border-radius: 3px;
411
+ }
412
+
413
+ .wplnst-results-mark-rechecked {
414
+ padding: 0px 1px;
415
+ background: #aa98a9;
416
+ border-color: #aa98a9;
417
+ }
418
+
419
+ .wplnst-results-mark-unlinked {
420
+ margin-right: 5px;
421
+ padding: 2px 3px 1px;
422
+ background: #8f9779;
423
+ border-color: #8f9779;
424
+ }
425
+
426
+ .wplnst-results-mark-modified {
427
+ background: #dd855c;
428
+ border-color: #dd855c;
429
+ }
430
+
431
+ .wplnst-results-mark-nofollow {
432
+ background: #f96161;
433
+ border-color: #f96161;
434
+ }
435
+
436
+ .wplnst-results-mark-relative {
437
+ background: #ff99cc;
438
+ border-color: #ff99cc;
439
+ }
440
+
441
+ .wplnst-results-mark-absolute {
442
+ background: #ff77aa;
443
+ border-color: #ff77aa;
444
+ }
445
+
446
+ .wplnst-results-mark-spaced {
447
+ background: #e1c348;
448
+ border-color: #e1c348;
449
+ }
450
+
451
+ .wplnst-results-mark-malformed {
452
+ background: #fe0000;
453
+ border-color: #fe0000;
454
+ }
455
+
456
+ .wplnst-results-mark-https {
457
+ background: #b5bc67;
458
+ border-color: #b5bc67;
459
+ }
460
+
461
+ .wplnst-results-mark-protorel {
462
+ background: #662f44;
463
+ border-color: #662f44;
464
+ }
465
+
466
+ .wplnst-results-mark-ignored {
467
+ background: #afbec0;
468
+ border-color: #afbec0;
469
+ }
470
+
471
+ .wplnst-results-mark-redirs {
472
+ background: #69b2ac;
473
+ border-color: #69b2ac;
474
+ }
475
+
476
+ .wplnst-results-anchor-mod {
477
+ margin: 1px 0;
478
+ }
479
+
480
+ .wplnst-results-url-marks {
481
+ margin: 2px 0 0;
482
+ }
483
+
484
+ .wplnst-results-url-marks .wplnst-results-mark {
485
+ margin-right: 5px;
486
+ }
487
+
488
+
489
+
490
+ /* levels menu */
491
+
492
+ #wplnst-levels-menu {
493
+ background: #f9f9f9;
494
+ border: 1px solid #e5e5e5;
495
+ padding: 2px 5px; margin: 0 0 1px;
496
+ -webkit-box-shadow: 0 1px 1px rgba(0,0,0,0.04);
497
+ box-shadow: 0 1px 1px rgba(0,0,0,0.04);
498
+ }
499
+
500
+ #wplnst-levels-menu.wplnst-levels-menu-isolated {
501
+ margin-bottom: 7px;
502
+ }
503
+
504
+ #wplnst-levels-menu .subsubsub {
505
+ margin: 0; padding: 0;
506
+ }
507
+
508
+ .wplnst-aproximate-total {
509
+ color: #777;
510
+ padding-top: 4px;
511
+ }
512
+
513
+
514
+
515
+ /* Extensions */
516
+
517
+ #wplnst-extensions {
518
+ width: 800px;
519
+ }
520
+
521
+ .wplnst-extensions-section {
522
+ margin-bottom: 50px;
523
+ }
524
+
525
+ #wplnst-extensions h3 {
526
+ font-size: 1.4em;
527
+ letter-spacing: -1px;
528
+ }
529
+
530
+ #wplnst-extensions p {
531
+ font-size: 1.3em;
532
+ }
533
+
534
+ #wplnst-extensions img {
535
+ border: 1px solid #ccc;
536
+ }
537
+
538
+
539
+
540
+ /* common classes */
541
+
542
+ .wplnst-display-none {
543
+ display: none;
544
+ }
545
+
546
+
547
+
548
+ /* clearfix custom class */
549
+
550
+ .wplnst-clearfix:after {
551
+ visibility: hidden;
552
+ display: block;
553
+ font-size: 0;
554
+ content: " ";
555
+ clear: both;
556
+ height: 0;
557
+ }
558
+ .wplnst-clearfix { display: inline-block; }
559
+ /* start commented backslash hack \*/
560
+ * html .wplnst-clearfix { height: 1%; }
561
+ .wplnst-clearfix { display: block; }
562
+ /* close commented backslash hack */
assets/images/advanced-anchor.png ADDED
Binary file
assets/images/advanced-close.png ADDED
Binary file
assets/images/advanced-extended.png ADDED
Binary file
assets/images/advanced-filters.png ADDED
Binary file
assets/images/advanced-reset.png ADDED
Binary file
assets/images/advanced-url.png ADDED
Binary file
assets/images/button-close-hover.png ADDED
Binary file
assets/images/button-close.png ADDED
Binary file
assets/images/close.png ADDED
Binary file
assets/images/extensions/pro-banner.jpg ADDED
Binary file
assets/images/extensions/wpls-web-crawler-edit.png ADDED
Binary file
assets/images/extensions/wpls-web-crawler-results-actions.png ADDED
Binary file
assets/images/extensions/wpls-web-crawler-results-advanced.png ADDED
Binary file
assets/images/extensions/wpls-web-crawler-url-tools-results.png ADDED
Binary file
assets/images/extensions/wpls-web-crawler-url-tools.png ADDED
Binary file
assets/images/loading.gif ADDED
Binary file
assets/images/scan-clock.png ADDED
Binary file
assets/images/scan-launch.png ADDED
Binary file
assets/images/scan-link.png ADDED
Binary file
assets/images/scan-warning.png ADDED
Binary file
assets/images/search.png ADDED
Binary file
assets/images/yes.png ADDED
Binary file
assets/js/admin-edit.js ADDED
@@ -0,0 +1,277 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ;jQuery(document).ready(function($) {
2
+
3
+
4
+
5
+ var elist_index = {};
6
+
7
+
8
+
9
+ function elist_arrange(names) {
10
+
11
+ var rq = /\%quot\%/g;
12
+ var n, name, field, editable, f;
13
+
14
+ for (n in names) {
15
+
16
+ name = names[n];
17
+ field = $('#wplnst-scan-' + name).val();
18
+ field = ('' === field)? [] : JSON.parse(field);
19
+ if (!(field instanceof Array) || 0 == field.length)
20
+ continue;
21
+
22
+ elist_index[name] = field.length;
23
+ editable = ('true' == $('#wplnst-elist-' + name).attr('data-editable'));
24
+
25
+ if ('custom-fields' == name) {
26
+ for (f in field)
27
+ elist_add_row(name, elist_get_row(name, {'name' : field[f]['name'].esc_html().replace(rq, '&quot;'), 'type' : field[f]['type'].esc_html().toUpperCase()}, editable), field[f]['index']);
28
+
29
+ } else if ('anchor-filters' == name) {
30
+ for (f in field)
31
+ elist_add_row(name, elist_get_row(name, {'value' : field[f]['value'].esc_html().replace(rq, '&quot;'), 'type' : field[f]['type'].esc_html().replace('-', ' ')}, editable), field[f]['index']);
32
+
33
+ } else if ('include-urls' == name || 'exclude-urls' == name) {
34
+ for (f in field)
35
+ elist_add_row(name, elist_get_row(name, {'value' : field[f]['value'].esc_html().replace(rq, '&quot;'), 'type' : field[f]['type'].esc_html().replace('-', ' ').replace('url ', 'URL ').replace(' url', ' URL').capitalize()}, editable), field[f]['index']);
36
+
37
+ } else if ('html-attributes' == name) {
38
+ for (f in field)
39
+ elist_add_row(name, elist_get_row('html-attributes', {'element' : field[f]['element'].esc_html(), 'having' : field[f]['having'].esc_html().replace('-', ' '), 'att' : field[f]['att'].esc_html().replace(rq, '&quot;'), 'op' : field[f]['op'].esc_html().replace('-', ' '), 'value' : field[f]['value'].esc_html().replace(rq, '&quot;')}, editable), field[f]['index']);
40
+ }
41
+ }
42
+ }
43
+
44
+
45
+
46
+ function elist_add(name, value, inputs, row) {
47
+
48
+ var field = $('#wplnst-scan-' + name).val();
49
+ field = ('' === field)? [] : JSON.parse(field);
50
+ if (!(field instanceof Array) || field.length > 25)
51
+ return;
52
+
53
+ (name in elist_index)? elist_index[name]++ : elist_index[name] = 0;
54
+ value['index'] = elist_index[name];
55
+ field.push(value);
56
+
57
+ $('#wplnst-scan-' + name).val(JSON.stringify(field));
58
+
59
+ for (var i = 0; i < inputs.length; i++)
60
+ $('#' + inputs[i]).val('');
61
+
62
+ elist_add_row(name, row, value['index']);
63
+ }
64
+
65
+
66
+
67
+ function elist_add_row(name, row, index) {
68
+ row = row.replace(/\%class\-index\%/g, 'wplnst-' + name + '-' + index);
69
+ row = row.replace('%close%', '<a href="#" class="wplnst-elist-close-link" data-name = "' + name + '" data-index="' + index + '">&nbsp;</a>');
70
+ $('#' + 'wplnst-elist-' + name).append(row);
71
+ $('#' + 'wplnst-elist-' + name).show();
72
+ }
73
+
74
+
75
+
76
+ function elist_get_row(name, args, editable) {
77
+
78
+ if ('custom-fields' == name)
79
+ return '<tr class="%class-index%"><td class="wplnst-elist-val wplnst-cfs-val">' + args['name'] + '</td><td class="wplnst-elist-type wplnst-cfs-type">' + args['type'] + '</td><td class="wplnst-elist-close">' + (editable? '%close%' : '') + '</td></tr><tr class="%class-index%"><td colspan="3" class="wplnst-elist-split"></td></tr>';
80
+
81
+ if ('anchor-filters' == name)
82
+ return '<tr class="%class-index%"><td class="wplnst-elist-type wplnst-afs-type">' + $('#wplnst-elist-anchor-filters').attr('data-label') + '&nbsp; <strong>' + args['type'] + '</strong></td><td class="wplnst-elist-val wplnst-afs-val">' + args['value'] + '</td><td class="wplnst-elist-close wplnst-afs-close">' + (editable? '%close%' : '') + '</td></tr><tr class="%class-index%"><td colspan="3" class="wplnst-elist-split"></td></tr>';
83
+
84
+ if ('include-urls' == name)
85
+ return '<tr class="%class-index%"><td class="wplnst-elist-val wplnst-ius-val">' + args['value'] + '</td><td class="wplnst-elist-type wplnst-ius-type">' + args['type'] + '</td><td class="wplnst-elist-close">' + (editable? '%close%' : '') + '</td></tr><tr class="%class-index%"><td colspan="3" class="wplnst-elist-split"></td></tr>';
86
+
87
+ if ('exclude-urls' == name)
88
+ return '<tr class="%class-index%"><td class="wplnst-elist-val wplnst-eus-val">' + args['value'] + '</td><td class="wplnst-elist-type wplnst-eus-type">' + args['type'] + '</td><td class="wplnst-elist-close">' + (editable? '%close%' : '') + '</td></tr><tr class="%class-index%"><td colspan="3" class="wplnst-elist-split"></td></tr>';
89
+
90
+ if ('html-attributes' == name)
91
+ return '<tr class="%class-index%"><td class="wplnst-elist-type wplnst-hes-ele">' + args['element'] + '</td><td class="wplnst-elist-type wplnst-hes-have">' + args['having'] + '</td><td class="wplnst-elist-val wplnst-hes-att">' + args['att'] + '</td><td class="wplnst-elist-type wplnst-hes-op">' + args['op'] + '</td><td class="wplnst-elist-val wplnst-hes-val">' + args['value'] + '</td><td class="wplnst-elist-close">' + (editable? '%close%' : '') + '</td></tr><tr class="%class-index%"><td colspan="3" class="wplnst-elist-split"></td></tr>';
92
+
93
+ return false;
94
+ }
95
+
96
+
97
+ $('.wplnst-elist').on('click', '.wplnst-elist-close-link', function() {
98
+
99
+ var name = $(this).attr('data-name');
100
+ var index = $(this).attr('data-index');
101
+
102
+ var field = $('#wplnst-scan-' + name).val();
103
+ if ('' !== field) {
104
+
105
+ field = JSON.parse(field);
106
+ if (field instanceof Array) {
107
+
108
+ var field_new = [];
109
+ for (var i = 0; i < field.length; i++) {
110
+ if (index != field[i]['index'])
111
+ field_new.push(field[i]);
112
+ }
113
+
114
+ $('#wplnst-scan-' + name).val(JSON.stringify(field_new));
115
+ $('.wplnst-' + name + '-' + index).remove();
116
+
117
+ if (0 === field_new.length)
118
+ $('#' + 'wplnst-elist-' + name).hide();
119
+ }
120
+ }
121
+
122
+ return false;
123
+ });
124
+
125
+
126
+
127
+ $('.wplnst-status-level').click(function() {
128
+ var level = $(this).attr('id').replace('ck-status-level-', '');
129
+ $('.wplnst-code-level-' + level).prop('checked', false);
130
+ });
131
+
132
+ $('.wplnst-code-level').click(function() {
133
+ var level = $(this).attr('id').replace('ck-status-code-', '').charAt(0);
134
+ $('#ck-status-level-' + level).prop('checked', false);
135
+ });
136
+
137
+
138
+
139
+ $('#wplnst-save-and-run').click(function() {
140
+ $('#wplnst-scan-run').val('1');
141
+ $('#wplnst-form').submit();
142
+ });
143
+
144
+
145
+
146
+ $('#wplnst-cf-new-add').click(function() {
147
+ var name = $('#wplnst-cf-new').val().trim();
148
+ if ('' === name)
149
+ return;
150
+ elist_add('custom-fields', {'name' : name, 'type': $('#wplnst-cf-new-type').val()}, ['wplnst-cf-new'], elist_get_row('custom-fields', {'name' : name.esc_html(), 'type' : $('#wplnst-cf-new-type option:selected').text().esc_html()}, true));
151
+ });
152
+
153
+ $('#wplnst-cf-new').bind('keypress', function(e) {
154
+ if (e.keyCode == 13) {
155
+ $('#wplnst-cf-new-add').click();
156
+ return false;
157
+ }
158
+ });
159
+
160
+
161
+
162
+ $('#wplnst-af-new-add').click(function() {
163
+ var value = $('#wplnst-af-new').val().trim();
164
+ var type = $('#wplnst-af-new-type').val();
165
+ if ('' === value && 'empty' != type)
166
+ return;
167
+ elist_add('anchor-filters', {'value' : ('empty' != type)? value : '', 'type': type}, ['wplnst-af-new'], elist_get_row('anchor-filters', {'type' : $('#wplnst-af-new-type option:selected').text().esc_html().toLowerCase(), 'value' : ('empty' != type)? value.esc_html() : ''}, true));
168
+ });
169
+
170
+ $('#wplnst-af-new').bind('keypress', function(e) {
171
+ if (e.keyCode == 13) {
172
+ $('#wplnst-af-new-add').click();
173
+ return false;
174
+ }
175
+ });
176
+
177
+ $('#wplnst-af-new-type').change(function() {
178
+ var disabled = ('empty' == $(this).val());
179
+ $('#wplnst-af-new').attr('disabled', disabled);
180
+ });
181
+
182
+
183
+
184
+ $('#wplnst-ius-new-add').click(function() {
185
+ var value = $('#wplnst-ius-new').val().trim();
186
+ if ('' === value)
187
+ return;
188
+ elist_add('include-urls', {'value' : value, 'type': $('#wplnst-ius-new-type').val()}, ['wplnst-ius-new'], elist_get_row('include-urls', {'value' : value.esc_html(), 'type' : $('#wplnst-ius-new-type option:selected').text().esc_html()}, true));
189
+ });
190
+
191
+ $('#wplnst-ius-new').bind('keypress', function(e) {
192
+ if (e.keyCode == 13) {
193
+ $('#wplnst-ius-new-add').click();
194
+ return false;
195
+ }
196
+ });
197
+
198
+
199
+
200
+ $('#wplnst-eus-new-add').click(function() {
201
+ var value = $('#wplnst-eus-new').val().trim();
202
+ if ('' === value)
203
+ return;
204
+ elist_add('exclude-urls', {'value' : value, 'type': $('#wplnst-eus-new-type').val()}, ['wplnst-eus-new'], elist_get_row('exclude-urls', {'value' : value.esc_html(), 'type' : $('#wplnst-eus-new-type option:selected').text().esc_html()}, true));
205
+ });
206
+
207
+ $('#wplnst-eus-new').bind('keypress', function(e) {
208
+ if (e.keyCode == 13) {
209
+ $('#wplnst-eus-new-add').click();
210
+ return false;
211
+ }
212
+ });
213
+
214
+
215
+
216
+ $('#wplnst-hes-new-add').click(function() {
217
+
218
+ var att = $('#wplnst-hes-new-att').val().trim();
219
+ if ('' === att)
220
+ return;
221
+
222
+ var element = $('#wplnst-hes-new').val();
223
+ var having = $('#wplnst-hes-new-have').val();
224
+
225
+ var op = op_text = value = '';
226
+ if ('have' == having) {
227
+
228
+ var op = $('#wplnst-hes-new-op').val();
229
+ var op_text = $('#wplnst-hes-new-op option:selected').text().toLowerCase();
230
+ var value = '' + $('#wplnst-hes-new-val').val().trim();
231
+
232
+ if ('not-empty' == op || 'empty' == op) {
233
+ value = '';
234
+ } else if ('' === value) {
235
+ return;
236
+ }
237
+ }
238
+
239
+ elist_add(
240
+ 'html-attributes',
241
+ {'att' : att, 'element' : element, 'having' : having, 'op' : op, 'value' : value},
242
+ ['wplnst-hes-new-att', 'wplnst-hes-new-val'],
243
+ elist_get_row('html-attributes', {'element' : element.esc_html(), 'having' : $('#wplnst-hes-new-have option:selected').text().toLowerCase().esc_html(), 'att' : att.esc_html(), 'op' : op_text.esc_html(), 'value' : value.esc_html()}, true)
244
+ );
245
+ });
246
+
247
+ $('#wplnst-hes-new-att').bind('keypress', function(e) {
248
+ if (e.keyCode == 13) {
249
+ $('#wplnst-hes-new-add').click();
250
+ return false;
251
+ }
252
+ });
253
+
254
+ $('#wplnst-hes-new-val').bind('keypress', function(e) {
255
+ if (e.keyCode == 13) {
256
+ $('#wplnst-hes-new-add').click();
257
+ return false;
258
+ }
259
+ });
260
+
261
+ $('#wplnst-hes-new-have').change(function() {
262
+ var disabled = ('have' != $(this).val());
263
+ $('#wplnst-hes-new-op').attr('disabled', disabled);
264
+ $('#wplnst-hes-new-val').attr('disabled', disabled);
265
+ });
266
+
267
+ $('#wplnst-hes-new-op').change(function() {
268
+ $('#wplnst-hes-new-val').attr('disabled', ('empty' == $(this).val() || 'not-empty' == $(this).val()));
269
+ });
270
+
271
+
272
+
273
+ elist_arrange(['custom-fields', 'anchor-filters', 'include-urls', 'exclude-urls', 'html-attributes']);
274
+
275
+
276
+
277
+ });
assets/js/admin.js ADDED
@@ -0,0 +1,138 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ;'use strict';
2
+
3
+ if (!String.prototype.trim) {(function() {
4
+ var r = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;
5
+ String.prototype.trim = function() {
6
+ return this.replace(r, '');
7
+ };
8
+ })();};
9
+
10
+ if (!String.prototype.esc_html) {(function() {
11
+ var r = /[&<>"'\/]/g;
12
+ var entity_map = {'&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;', '/': '&#x2F;'};
13
+ String.prototype.esc_html = function() {
14
+ return this.replace(r, function from_entity_map(s) {
15
+ return entity_map[s];
16
+ });
17
+ };
18
+ })();};
19
+
20
+ if (!String.prototype.capitalize) {
21
+ String.prototype.capitalize = function() {
22
+ return this.charAt(0).toUpperCase() + this.slice(1);
23
+ }
24
+ };
25
+
26
+ jQuery(document).ready(function($) {
27
+
28
+
29
+
30
+ $('#wplnst-tabs-nav').find('a.nav-tab').click(function() {
31
+ $('#wplnst-tabs-nav').find('a').removeClass('nav-tab-active');
32
+ $('.wplnst-tab').removeClass('wplnst-tab-active');
33
+ var id = $(this).attr('id').replace('-tab', '');
34
+ $('#' + id).addClass('wplnst-tab-active');
35
+ $(this).addClass('nav-tab-active');
36
+ var action = $('#wplnst-form').attr('action').split('#top#');
37
+ $('#wplnst-form').attr('action', action[0] + '#top#' + id);
38
+ });
39
+
40
+ if ($('.wplnst-tab-default').length) {
41
+ var active_tab = window.location.hash.replace('#top', '').replace('#wplnst-', '');
42
+ if ('' === active_tab || active_tab === '#_=_')
43
+ active_tab = $('.wplnst-tab-default').attr('id').replace('wplnst-', '');
44
+ $('#wplnst-' + active_tab).addClass('wplnst-tab-active');
45
+ $('#wplnst-' + active_tab + '-tab').addClass('nav-tab-active');
46
+ $('#wplnst-' + active_tab + '-tab').click();
47
+ if ('general' == active_tab && 0 == $('#wplnst-scan-id').val())
48
+ $('#tx-name').focus();
49
+ }
50
+
51
+ $('.wplnst-scan-delete').click(function() {
52
+ if (confirm($('#wplnst-scans').attr('data-confirm-delete'))) {
53
+ $(this).attr('href', $(this).attr('href') + '&confirm=on');
54
+ return true;
55
+ }
56
+ return false;
57
+ });
58
+
59
+ $('.wplnst-scan-delete-isolated').click(function() {
60
+ if (confirm($(this).attr('data-confirm-delete'))) {
61
+ $(this).attr('href', $(this).attr('href') + '&confirm=on');
62
+ return true;
63
+ }
64
+ return false;
65
+ });
66
+
67
+
68
+
69
+ $('.wplnst-remove-entry').click(function() {
70
+ return confirm($('#wplnst-results').attr('data-confirm-delete-entry'));
71
+ });
72
+
73
+ $('.wplnst-remove-comment').click(function() {
74
+ return confirm($('#wplnst-results').attr('data-confirm-delete-comment'));
75
+ });
76
+
77
+ $('.wplnst-remove-bookmark').click(function() {
78
+ return confirm($('#wplnst-results').attr('data-confirm-delete-bookmark'));
79
+ });
80
+
81
+
82
+
83
+
84
+ $('.wplnst-scans-bulkactions-top .button.action').click(function() {
85
+ scans_bulkactions($('#bulk-action-selector-top').val());
86
+ return false;
87
+ });
88
+
89
+ $('.wplnst-scans-bulkactions-bottom .button.action').click(function() {
90
+ scans_bulkactions($('#bulk-action-selector-bottom').val());
91
+ return false;
92
+ });
93
+
94
+ function scans_bulkactions(action) {
95
+
96
+ if ('delete' == action) {
97
+
98
+ var checked = [];
99
+ $('input[type=checkbox].wplnst-ck-scan-id').each(function() {
100
+ if ($(this).is(':checked'))
101
+ checked.push($(this).val());
102
+ });
103
+
104
+ if (checked.length > 0) {
105
+ if (confirm($('#wplnst-scans').attr('data-confirm-delete-bulk')))
106
+ window.location.href = $('#wplnst-scans').attr('data-href-delete').replace('%scan_id%', '-' + checked.join('-')) + '&confirm=on';
107
+ }
108
+ }
109
+ }
110
+
111
+
112
+
113
+ $('#wplnst-filter-button').click(function() {
114
+ var args = '', value;
115
+ var fields = $(this).attr('data-fields').split(',');
116
+ for (var i in fields) {
117
+ value = $('#wplnst-filter-' + fields[i]).val();
118
+ if (typeof value != 'undefined' && '' !== value)
119
+ args += '&' + fields[i] + '=' + value;
120
+ }
121
+ window.location.href = $(this).attr('data-href') + args;
122
+ return false;
123
+ });
124
+
125
+
126
+
127
+ $('#current-page-selector').keydown(function(e) {
128
+ if (e.keyCode == 13) {
129
+ var paged = parseInt($(this).val(), 10);
130
+ var pages = parseInt($('.total-pages').first().text(), 10);
131
+ window.location.href = $(this).closest('form').attr('action') + ((paged > 1)? '&paged=' + ((paged > pages)? pages : paged) : '');
132
+ return false;
133
+ }
134
+ });
135
+
136
+
137
+
138
+ });
assets/js/lightboxed/LICENSE ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ Licensed under the Apache License, Version 2.0 (the "License");
2
+ you may not use this file except in compliance with the License.
3
+ You may obtain a copy of the License at
4
+
5
+ http://www.apache.org/licenses/LICENSE-2.0
6
+
7
+ Unless required by applicable law or agreed to in writing, software
8
+ distributed under the License is distributed on an "AS IS" BASIS,
9
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10
+ See the License for the specific language governing permissions and
11
+ limitations under the License.
assets/js/lightboxed/jquery.lightboxed.js ADDED
@@ -0,0 +1,273 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ * $ lightboxed 1.0
3
+ * By Pau Iglesias on seedplugins.com
4
+ *
5
+ * Based on lightbox_me 2.4 by Buck Wilson
6
+ *
7
+ * Licensed under the Apache License, Version 2.0 (the "License");
8
+ * you may not use this file except in compliance with the License.
9
+ * You may obtain a copy of the License at
10
+ *
11
+ * http://www.apache.org/licenses/LICENSE-2.0
12
+ *
13
+ * Unless required by applicable law or agreed to in writing, software
14
+ * distributed under the License is distributed on an "AS IS" BASIS,
15
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ * See the License for the specific language governing permissions and
17
+ * limitations under the License.
18
+ */
19
+
20
+ (function($) {
21
+
22
+
23
+
24
+ var zIndex = false, fronts = [];
25
+
26
+
27
+
28
+ $(document).on('keyup', observeKeyPress);
29
+
30
+ function observeKeyPress(e) {
31
+ if (fronts.length && (e.keyCode == 27 || (e.DOM_VK_ESCAPE == 27 && e.which==0)))
32
+ fronts[fronts.length - 1].trigger('escPress');
33
+ }
34
+
35
+
36
+
37
+
38
+ $.fn.wplnst_lightboxed = function(options) {
39
+
40
+ return this.each(function() {
41
+
42
+
43
+
44
+ var settings = $.extend({}, $.fn.wplnst_lightboxed.defaults, options);
45
+ var $self = $(this), $overlay;
46
+
47
+
48
+
49
+ function init() {
50
+
51
+ fronts.push($self);
52
+ $('body').append($self.hide());
53
+
54
+ zIndex = (false === zIndex)? settings.zIndex : zIndex + 1;
55
+
56
+ if (settings.showOverlay) {
57
+
58
+ $overlay = $('<div class="' + settings.classPrefix + '_overlay"/>');
59
+
60
+ $('body').append($overlay);
61
+
62
+ setOverlayHeight();
63
+ $(window).resize(setOverlayHeight);
64
+
65
+ $overlay.css({
66
+ position : 'absolute',
67
+ width : '100%',
68
+ top : 0,
69
+ left : 0,
70
+ right : 0,
71
+ bottom : 0,
72
+ zIndex : zIndex,
73
+ display : 'none',
74
+ });
75
+
76
+ $overlay.css(settings.overlayCSS);
77
+
78
+ $overlay.fadeIn(settings.overlaySpeed, function() {
79
+ setSelfPosition();
80
+ $self[settings.appearEffect](settings.lightboxSpeed, function() {
81
+ raiseOnLoad();
82
+ if (settings.closeClickOutside) {
83
+ $overlay.click(function(e) {
84
+ closeLightbox();
85
+ e.preventDefault;
86
+ return false;
87
+ });
88
+ }
89
+ });
90
+ });
91
+
92
+ } else {
93
+
94
+ setSelfPosition();
95
+ $self[settings.appearEffect](settings.lightboxSpeed, function() {
96
+ $self.show();
97
+ raiseOnLoad();
98
+ if (settings.closeClickOutside)
99
+ $(document).on('click', onClickOutside);
100
+ });
101
+ }
102
+
103
+ if (settings.parentLightbox)
104
+ settings.parentLightbox.fadeOut(200);
105
+
106
+ $(window).resize(setSelfPosition).scroll(setSelfPosition);
107
+ $(document).on('click', settings.closeSelector, onCloseSelector);
108
+
109
+ $self.on('close', closeLightbox);
110
+ $self.on('escPress', onEscPress);
111
+ $self.on('reposition', setSelfPosition);
112
+ }
113
+
114
+
115
+
116
+ function closeLightbox() {
117
+
118
+ fronts.pop();
119
+
120
+ if (settings.showOverlay) {
121
+ $overlay.remove();
122
+ $(window).unbind('resize', setOverlayHeight);
123
+ } else if (settings.closeClickOutside) {
124
+ $(document).off('click', onClickOutside);
125
+ }
126
+
127
+ if (settings.parentLightbox)
128
+ settings.parentLightbox.fadeIn(200);
129
+
130
+ if (settings.preventScroll)
131
+ $('body').css('overflow', '');
132
+
133
+ $(document).off('click', settings.closeSelector, onCloseSelector);
134
+
135
+ $self.off('close', closeLightbox);
136
+ $self.off('escPress', onEscPress);
137
+ $self.off('reposition', setSelfPosition);
138
+
139
+ $(window).unbind('resize', setSelfPosition);
140
+ $(window).unbind('scroll', setSelfPosition);
141
+
142
+ $self[settings.disappearEffect](settings.lightboxSpeed, function() {
143
+ raiseOnClose();
144
+ });
145
+
146
+ //$self.hide();
147
+ }
148
+
149
+
150
+
151
+ function setOverlayHeight() {
152
+ ($(window).height() < $(document).height())? $overlay.css({ height : $(document).height() + 'px' }) : $overlay.css({ height : '100%' });
153
+ }
154
+
155
+
156
+
157
+ function setSelfPosition() {
158
+
159
+ $self.css({
160
+ left : '50%',
161
+ marginLeft : ($self.outerWidth() / 2) * -1,
162
+ zIndex : zIndex + 1
163
+ });
164
+
165
+ if (($self.height() + 80 >= $(window).height()) && ($self.css('position') != 'absolute')) {
166
+
167
+ var topOffset = $(document).scrollTop() + 40;
168
+
169
+ $self.css({
170
+ position : 'absolute',
171
+ top : topOffset + 'px',
172
+ marginTop : 0
173
+ });
174
+
175
+ } else if ($self.height() + 80 < $(window).height()) {
176
+
177
+ settings.centered? $self.css({
178
+ position : 'fixed',
179
+ top : '50%',
180
+ marginTop : ($self.outerHeight() / 2) * -1
181
+ }) : $self.css({
182
+ position : 'fixed'
183
+ }).css(settings.modalCSS);
184
+
185
+ if (settings.preventScroll)
186
+ $('body').css('overflow', 'hidden');
187
+ }
188
+ }
189
+
190
+
191
+
192
+ function onEscPress() {
193
+ if (settings.closeEsc)
194
+ closeLightbox();
195
+ }
196
+
197
+
198
+
199
+ function onClickOutside(e) {
200
+ if (!$self.is(e.target) && 0 === $self.has(e.target).length) {
201
+ closeLightbox();
202
+ e.preventDefault;
203
+ return false;
204
+ }
205
+ }
206
+
207
+
208
+
209
+ function onCloseSelector(e) {
210
+ if ($self.has(e.target).length) {
211
+ closeLightbox();
212
+ e.preventDefault();
213
+ return false;
214
+ }
215
+ }
216
+
217
+
218
+
219
+ function raiseOnLoad() {
220
+ $self.trigger('lightboxedLoad');
221
+ $self.off('lightboxedLoad');
222
+ }
223
+
224
+
225
+
226
+ function raiseOnClose() {
227
+ $self.trigger('lightboxedClose');
228
+ $self.off('lightboxedClose');
229
+ }
230
+
231
+
232
+
233
+ // Start
234
+ init();
235
+
236
+
237
+
238
+ });
239
+ };
240
+
241
+
242
+
243
+ $.fn.wplnst_lightboxed.defaults = {
244
+
245
+ // Animation
246
+ appearEffect : 'fadeIn',
247
+ appearEase : '',
248
+ disappearEffect : 'fadeOut',
249
+ disappearEase : '',
250
+ lightboxSpeed : 200,
251
+ overlaySpeed : 250,
252
+
253
+ // Close
254
+ closeSelector : '.wplnst_lightboxed_close',
255
+ closeClickOutside : true,
256
+ closeEsc : true,
257
+
258
+ // Behavior
259
+ showOverlay : true,
260
+ parentLightbox : false,
261
+ preventScroll : true,
262
+
263
+ // Style
264
+ centered : false,
265
+ classPrefix : 'wplnst_lbx',
266
+ zIndex : 9999,
267
+ modalCSS : { top : '40px' },
268
+ overlayCSS : { background : 'black', opacity : .3 }
269
+ }
270
+
271
+
272
+
273
+ })(jQuery);
assets/js/lightboxed/jquery.lightboxed.min.js ADDED
@@ -0,0 +1 @@
 
1
+ !function(e){function o(e){n.length&&(27==e.keyCode||27==e.DOM_VK_ESCAPE&&0==e.which)&&n[n.length-1].trigger("escPress")}var t=!1,n=[];e(document).on("keyup",o),e.fn.wplnst_lightboxed=function(o){return this.each(function(){function i(){n.push(g),e("body").append(g.hide()),t=!1===t?u.zIndex:t+1,u.showOverlay?(h=e('<div class="'+u.classPrefix+'_overlay"/>'),e("body").append(h),c(),e(window).resize(c),h.css({position:"absolute",width:"100%",top:0,left:0,right:0,bottom:0,zIndex:t,display:"none"}),h.css(u.overlayCSS),h.fadeIn(u.overlaySpeed,function(){l(),g[u.appearEffect](u.lightboxSpeed,function(){f(),u.closeClickOutside&&h.click(function(e){return s(),e.preventDefault,!1})})})):(l(),g[u.appearEffect](u.lightboxSpeed,function(){g.show(),f(),u.closeClickOutside&&e(document).on("click",d)})),u.parentLightbox&&u.parentLightbox.fadeOut(200),e(window).resize(l).scroll(l),e(document).on("click",u.closeSelector,a),g.on("close",s),g.on("escPress",r),g.on("reposition",l)}function s(){n.pop(),u.showOverlay?(h.remove(),e(window).unbind("resize",c)):u.closeClickOutside&&e(document).off("click",d),u.parentLightbox&&u.parentLightbox.fadeIn(200),u.preventScroll&&e("body").css("overflow",""),e(document).off("click",u.closeSelector,a),g.off("close",s),g.off("escPress",r),g.off("reposition",l),e(window).unbind("resize",l),e(window).unbind("scroll",l),g[u.disappearEffect](u.lightboxSpeed,function(){p()})}function c(){e(window).height()<e(document).height()?h.css({height:e(document).height()+"px"}):h.css({height:"100%"})}function l(){if(g.css({left:"50%",marginLeft:g.outerWidth()/2*-1,zIndex:t+1}),g.height()+80>=e(window).height()&&"absolute"!=g.css("position")){var o=e(document).scrollTop()+40;g.css({position:"absolute",top:o+"px",marginTop:0})}else g.height()+80<e(window).height()&&(u.centered?g.css({position:"fixed",top:"50%",marginTop:g.outerHeight()/2*-1}):g.css({position:"fixed"}).css(u.modalCSS),u.preventScroll&&e("body").css("overflow","hidden"))}function r(){u.closeEsc&&s()}function d(e){return g.is(e.target)||0!==g.has(e.target).length?void 0:(s(),e.preventDefault,!1)}function a(e){return g.has(e.target).length?(s(),e.preventDefault(),!1):void 0}function f(){g.trigger("lightboxedLoad"),g.off("lightboxedLoad")}function p(){g.trigger("lightboxedClose"),g.off("lightboxedClose")}var h,u=e.extend({},e.fn.wplnst_lightboxed.defaults,o),g=e(this);i()})},e.fn.wplnst_lightboxed.defaults={appearEffect:"fadeIn",appearEase:"",disappearEffect:"fadeOut",disappearEase:"",lightboxSpeed:200,overlaySpeed:250,closeSelector:".wplnst_lightboxed_close",closeClickOutside:!0,closeEsc:!0,showOverlay:!0,parentLightbox:!1,preventScroll:!0,centered:!1,classPrefix:"wplnst_lbx",zIndex:9999,modalCSS:{top:"40px"},overlayCSS:{background:"black",opacity:.3}}}(jQuery);
core/alive.php ADDED
@@ -0,0 +1,531 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * WP Link Status Core Alive class
5
+ *
6
+ * @package WP Link Status
7
+ * @subpackage WP Link Status Core
8
+ */
9
+ class WPLNST_Core_Alive {
10
+
11
+
12
+
13
+ // Checks and launch crawler procedures
14
+ // ---------------------------------------------------------------------------------------------------
15
+
16
+
17
+
18
+ /**
19
+ * Start the check
20
+ */
21
+ public static function check() {
22
+ self::start(get_class());
23
+ }
24
+
25
+
26
+
27
+ /**
28
+ * Check running crawler
29
+ */
30
+ public static function start($source) {
31
+
32
+ // Load util file first
33
+ require_once(WPLNST_PATH.'/core/util.php');
34
+
35
+ // Version components
36
+ $source::start_version();
37
+
38
+ // Load common plugin dependencies
39
+ wplnst_require('core', 'types');
40
+
41
+ // Load custom nonce system
42
+ wplnst_require('core', 'nonce/nonce');
43
+
44
+ // Check crawl request arguments
45
+ if (!(defined('DOING_AJAX') && DOING_AJAX) || empty($_GET['wplnst_crawler']) || empty($_GET['wplnst_nonce']) || empty($_GET['wplnst_slug'])) {
46
+
47
+ // Check notifications
48
+ if (!empty($_GET['wplnst_notify_email']) && 'on' == $_GET['wplnst_notify_email']) {
49
+
50
+ // And finally check notify nonce
51
+ if (!empty($_GET['wplnst_notify_nonce']) && self::verify_notify_nonce($_GET['wplnst_notify_nonce']))
52
+ add_action('plugins_loaded', array($source, 'notify'));
53
+
54
+ // Check cURL needed before
55
+ } elseif (wplnst_is_curl_enabled()) {
56
+
57
+ // Activity checks
58
+ self::activity();
59
+ }
60
+
61
+ // Check this install crawler slug
62
+ } elseif ($_GET['wplnst_slug'] == self::get_crawler_slug()) {
63
+
64
+ /**
65
+ * From here we are under crawler scope,
66
+ * based on admin-ajax mode and special URL arguments.
67
+ */
68
+
69
+ // Check cURL loaded
70
+ if (wplnst_is_curl_enabled()) {
71
+
72
+ // Check scan integer and nonce argument
73
+ $scan_id = (int) $_GET['wplnst_crawler'];
74
+ if ($scan_id > 0) {
75
+
76
+ // Check possible thread
77
+ $thread_id = isset($_GET['wplnst_thread'])? $_GET['wplnst_thread'] : false;
78
+
79
+ // Check scan and nonce
80
+ if (false === ($scan_row = self::verify_crawler($scan_id, $thread_id, $_GET['wplnst_nonce']))) {
81
+
82
+ // Debug
83
+ wplnst_debug('scan '.$scan_id.' - thread '.(empty($thread_id)? 'false' : $thread_id).' - verify crawler error', 'alive');
84
+
85
+ // Threads control
86
+ } elseif (false === ($thread_id = self::threading($scan_row, $thread_id))) {
87
+
88
+ // Debug
89
+ wplnst_debug('scan '.$scan_id.' - thread '.$thread_id.' - threading error', 'alive');
90
+
91
+ // Ok
92
+ } else {
93
+
94
+ // Run instance of crawler
95
+ $source::instantiate_crawler($scan_row, $thread_id);
96
+ }
97
+ }
98
+ }
99
+
100
+ // Debug end
101
+ wplnst_debug((empty($scan_row)? '' : 'scan '.$scan_row->scan_id.' - ').'thread '.(empty($thread_id)? '' : $thread_id).' terminate', 'alive');
102
+
103
+ // End
104
+ die;
105
+ }
106
+ }
107
+
108
+
109
+
110
+ /**
111
+ * Specific version components at start
112
+ */
113
+ protected static function start_version() {
114
+
115
+ // Plugin definitions
116
+ wplnst_require('core', 'plugin');
117
+ }
118
+
119
+
120
+
121
+ /**
122
+ * Start or continue active scan
123
+ */
124
+ public static function run($scan_id, $hash, $thread_id = false) {
125
+
126
+ // Debug point
127
+ wplnst_debug('scan '.$scan_id.' - '.(empty($thread_id)? '' : 'thread '.$thread_id.' ').'run', 'alive');
128
+
129
+ // Check salt file
130
+ WPLNST_Core_Nonce::check_salt_file();
131
+
132
+ // Load cURL wrapper library
133
+ wplnst_require('core', 'curl');
134
+
135
+ // Spawn crawler call
136
+ WPLNST_Core_CURL::spawn(array(
137
+ 'CURLOPT_URL' => self::get_crawler_url($scan_id, $hash, $thread_id),
138
+ 'CURLOPT_USERAGENT' => wplnst_get_tsetting('user_agent'),
139
+ ));
140
+ }
141
+
142
+
143
+
144
+ /**
145
+ * Check last active scan activity and re-active scan if needed.
146
+ * This check runs in a normal WP execution, and not under any special URL,
147
+ * except at the end of a crawler process to check queued scans.
148
+ */
149
+ public static function activity($skip_check = false) {
150
+
151
+ // Skip checks from the end
152
+ if (!$skip_check) {
153
+
154
+ // Avoid checks in plugins page
155
+ if (is_admin() && false !== stripos($_SERVER['REQUEST_URI'], '/plugins.php'))
156
+ return;
157
+
158
+ // Preliminary check to avoid constant database queries
159
+ $timestamp = (int) get_option('wplnst_crawler_timestamp');
160
+ if ($timestamp > 0 && time() - $timestamp <= wplnst_get_nsetting('crawler_alive'))
161
+ return;
162
+
163
+ // Reset timer, and exit if this is the first time
164
+ update_option('wplnst_crawler_timestamp', time());
165
+ if (empty($timestamp))
166
+ return;
167
+ }
168
+
169
+ // Retrieve active scans
170
+ $max_scans = wplnst_get_nsetting('max_scans');
171
+ $scans = self::get_ready_scans('play', 'started_at ASC', $max_scans);
172
+
173
+ // Check queued merge
174
+ if (count($scans) < $max_scans) {
175
+ $scans_queued = self::get_ready_scans('queued', 'enqueued_at ASC', $max_scans - count($scans));
176
+ if (!empty($scans_queued))
177
+ $scans = array_merge($scans, $scans_queued);
178
+ }
179
+
180
+ // Globals
181
+ global $wpdb;
182
+
183
+ // Enum active scans
184
+ foreach ($scans as $scan_info) {
185
+
186
+ // Check threads cuota
187
+ $threads = @json_decode($scan_info->threads, true);
188
+ if (!empty($threads) && is_array($threads)) {
189
+
190
+ // Initialize
191
+ $active = 0;
192
+
193
+ // Total timeout and minor correction
194
+ $total_timeout = wplnst_get_nsetting('connect_timeout', $scan_info->connect_timeout) + wplnst_get_nsetting('request_timeout', $scan_info->request_timeout) + wplnst_get_nsetting('extra_timeout');
195
+
196
+ // Count active threads
197
+ foreach ($threads as $thread_id => $thread) {
198
+ if ('on' == $thread['status']) {
199
+ if ((time() - (int) $thread['timestamp']) <= $total_timeout)
200
+ $active++;
201
+ }
202
+ }
203
+
204
+ // Check free limits
205
+ if ($active >= wplnst_get_nsetting('max_threads', $scan_info->max_threads))
206
+ continue;
207
+ }
208
+
209
+ // Check and cast queued to play
210
+ if ('queued' == $scan_info->status) {
211
+
212
+ // Configure update
213
+ $update = array('status' => 'play');
214
+ $current_time = current_time('mysql', true);
215
+
216
+ // First scan play
217
+ if (empty($scan_info->started_at) || '0000-00-00 00:00:00' == $scan_info->started_at) {
218
+ $update['started_at'] = $current_time;
219
+ $update['stopped_at'] = '0000-00-00 00:00:00';
220
+
221
+ // Continue
222
+ } else {
223
+
224
+ // Play again
225
+ $update['continued_at'] = $current_time;
226
+ }
227
+
228
+ // And update
229
+ $wpdb->update($wpdb->prefix.'wplnst_scans', $update, array('scan_id' => $scan_info->scan_id));
230
+ }
231
+
232
+ // Debug output
233
+ wplnst_debug('scan '.$scan_info->scan_id.' launch from activity()', 'alive');
234
+
235
+ // New thread
236
+ self::run($scan_info->scan_id, $scan_info->hash);
237
+ }
238
+ }
239
+
240
+
241
+
242
+ // Internal procedures
243
+ // ---------------------------------------------------------------------------------------------------
244
+
245
+
246
+
247
+ /**
248
+ * Control current thread and check for free threads
249
+ * This check runs under special URL dedicated to threading
250
+ */
251
+ private static function threading($scan_row, $current_thread_id) {
252
+
253
+ // Globals
254
+ global $wpdb;
255
+
256
+ // Check threads array
257
+ $threads = @json_decode($scan_row->threads, true);
258
+ $threads = (empty($threads) || !is_array($threads))? array() : $threads;
259
+
260
+ // Check invalid thread id
261
+ if (!empty($current_thread_id) && !isset($threads[$current_thread_id]))
262
+ return false;
263
+
264
+ // Initialize
265
+ $active = 0;
266
+ $new_thread_id = false;
267
+
268
+ // Total timeout and minor correction
269
+ $total_timeout = wplnst_get_nsetting('connect_timeout', $scan_row->connect_timeout) + wplnst_get_nsetting('request_timeout', $scan_row->request_timeout) + wplnst_get_nsetting('extra_timeout');
270
+
271
+ // Max crawler threads allowed
272
+ $max_threads = wplnst_get_nsetting('max_threads', $scan_row->max_threads);
273
+
274
+ // Inspect total active threads
275
+ foreach ($threads as $thread_id => $thread) {
276
+
277
+ // No checks for current thread
278
+ if (!empty($current_thread_id) && $current_thread_id == $thread_id) {
279
+ $active++;
280
+
281
+ // Only active threads
282
+ } elseif ('on' == $thread['status']) {
283
+
284
+ // Timestamp limit
285
+ if ((time() - (int) $thread['timestamp']) > $total_timeout) {
286
+ $threads[$thread_id]['status'] = 'off';
287
+
288
+ // Active
289
+ } else {
290
+ $active++;
291
+ }
292
+ }
293
+ }
294
+
295
+ // No current thread allowed
296
+ if ($active > $max_threads) {
297
+ if (!empty($current_thread_id))
298
+ $threads[$current_thread_id]['status'] = 'off';
299
+ $current_thread_id = false;
300
+
301
+ // Check new thread
302
+ } elseif ($active < $max_threads) {
303
+ $active++;
304
+ $new_thread_id = empty($threads)? 1 : (int) max(array_keys($threads)) + 1;
305
+ $threads[$new_thread_id] = array('status' => 'on', 'timestamp' => time());
306
+ }
307
+
308
+ // Rebuild current thread
309
+ if (!empty($current_thread_id))
310
+ $threads[$current_thread_id] = array('status' => 'on', 'timestamp' => time());
311
+
312
+ // Update threads data
313
+ $wpdb->update($wpdb->prefix.'wplnst_scans', array('threads' => @json_encode($threads)), array('scan_id' => $scan_row->scan_id));
314
+
315
+ // Launch new thread
316
+ if (!empty($new_thread_id) && $active < $max_threads)
317
+ self::run($scan_row->scan_id, $scan_row->hash);
318
+
319
+ // Done
320
+ return empty($current_thread_id)? $new_thread_id : $current_thread_id;
321
+ }
322
+
323
+
324
+
325
+ // Active scans info
326
+ // ---------------------------------------------------------------------------------------------------
327
+
328
+
329
+
330
+ /**
331
+ * Retrieve active scan row identifier, ready state and threads value
332
+ */
333
+ private static function get_ready_scans($status, $order, $max_scans) {
334
+
335
+ // Globals
336
+ global $wpdb;
337
+
338
+ // Retrieve scans
339
+ $scans = $wpdb->get_results($wpdb->prepare('SELECT SQL_NO_CACHE scan_id, status, ready, hash, started_at, threads, max_threads, connect_timeout, request_timeout FROM '.$wpdb->prefix.'wplnst_scans WHERE ready = 1 AND status = %s ORDER BY '.esc_sql($order), $status));
340
+
341
+ // Check results
342
+ if (empty($scans) || !is_array($scans))
343
+ return array();
344
+
345
+ // Initialize
346
+ $index = 0;
347
+ $allowed = array();
348
+
349
+ // Enum scans
350
+ foreach ($scans as $scan) {
351
+
352
+ // Check index
353
+ $index++;
354
+ if ($index > $max_scans) {
355
+
356
+ // Queue scan
357
+ if ('play' == $scan->status) {
358
+ $current_time = current_time('mysql', true);
359
+ $wpdb->query($wpdb->prepare('UPDATE '.$wpdb->prefix.'wplnst_scans SET status = "queued", enqueued_at = %s, stopped_at = %s WHERE scan_id = %d', $current_time, $current_time, $scan->scan_id));
360
+ }
361
+
362
+ // Next
363
+ continue;
364
+ }
365
+
366
+ // Add allowed
367
+ $allowed[] = $scan;
368
+ }
369
+
370
+ // Preserve allowed scans
371
+ return $allowed;
372
+ }
373
+
374
+
375
+
376
+ // Crawling related data
377
+ // ---------------------------------------------------------------------------------------------------
378
+
379
+
380
+
381
+ /**
382
+ * Verify if crawler exists, is ready, and nonce
383
+ */
384
+ private static function verify_crawler($scan_id, $thread_id, $nonce) {
385
+
386
+ // Globals
387
+ global $wpdb;
388
+
389
+ // Retrieve and check scan
390
+ $scan_row = $wpdb->get_row($wpdb->prepare('SELECT SQL_NO_CACHE scan_id, status, ready, hash, threads, max_threads, connect_timeout, request_timeout FROM '.$wpdb->prefix.'wplnst_scans WHERE scan_id = %d', $scan_id));
391
+ if (empty($scan_row) || !is_object($scan_row))
392
+ return false;
393
+
394
+ // Check scan status
395
+ if (empty($scan_row->status) || 'wait' == $scan_row->status || 'end' == $scan_row->status)
396
+ return false;
397
+
398
+ // Check scan ready
399
+ if (empty($scan_row->ready) || 1 != (int) $scan_row->ready)
400
+ return false;
401
+
402
+ // Check nonce and return scan
403
+ return self::verify_crawler_nonce($nonce, $scan_row->hash, $thread_id)? $scan_row : false;
404
+ }
405
+
406
+
407
+
408
+ /**
409
+ * Load a crawler instance
410
+ */
411
+ protected static function instantiate_crawler($scan_row, $thread_id) {
412
+
413
+ // Load dependencies
414
+ wplnst_require('core', 'crawler');
415
+
416
+ // Instance
417
+ WPLNST_Core_Crawler::instantiate(array(
418
+ 'scan_id' => $scan_row->scan_id,
419
+ 'thread_id' => $thread_id,
420
+ 'crawler_url' => self::get_crawler_url($scan_row->scan_id, $scan_row->hash, $thread_id),
421
+ ));
422
+ }
423
+
424
+
425
+
426
+ /**
427
+ * Verify crawler nonce from scan id
428
+ */
429
+ private static function verify_crawler_nonce($value, $hash, $thread_id = false) {
430
+ return WPLNST_Core_Nonce::verify_nonce($value, 'crawl-scan-'.$hash.(empty($thread_id)? '' : '-thread-'.$thread_id.'-'.self::get_crawler_slug()));
431
+ }
432
+
433
+
434
+
435
+ /**
436
+ * Create nonce for the crawler
437
+ */
438
+ private static function get_crawler_nonce($hash, $thread_id = false) {
439
+ return WPLNST_Core_Nonce::create_nonce('crawl-scan-'.$hash.(empty($thread_id)? '' : '-thread-'.$thread_id.'-'.self::get_crawler_slug()));
440
+ }
441
+
442
+
443
+
444
+ /**
445
+ * Compose current crawler URL
446
+ */
447
+ public static function get_crawler_url($scan_id, $hash, $thread_id = false) {
448
+
449
+ // Default args
450
+ $args = array(
451
+ 'wplnst_crawler' => $scan_id,
452
+ 'wplnst_nonce' => self::get_crawler_nonce($hash, $thread_id),
453
+ 'wplnst_slug' => self::get_crawler_slug(),
454
+ );
455
+
456
+ // Check thread arg
457
+ if (!empty($thread_id))
458
+ $args['wplnst_thread'] = $thread_id;
459
+
460
+ // Compose URL
461
+ return add_query_arg($args, rtrim(admin_url('admin-ajax.php'), '/'));
462
+ }
463
+
464
+
465
+
466
+ /**
467
+ * Retrieve, check or generate unique crawler slug
468
+ */
469
+ public static function get_crawler_slug() {
470
+
471
+ // Retrieve current slug
472
+ $crawler_slug = get_option('wplnst_crawler_slug');
473
+
474
+ // Check valid page slug
475
+ if (empty($crawler_slug) || 16 != strlen($crawler_slug) || !preg_match('/^[a-z0-9]+$/', $crawler_slug)) {
476
+
477
+ // Generate new slug
478
+ $crawler_slug = strtolower(WPLNST_Core_Nonce::generate_password(16, false, false));
479
+
480
+ // And update
481
+ update_option('wplnst_crawler_slug', $crawler_slug);
482
+ }
483
+
484
+ // Done
485
+ return $crawler_slug;
486
+ }
487
+
488
+
489
+
490
+ // Notifications launchers
491
+ // ---------------------------------------------------------------------------------------------------
492
+
493
+
494
+
495
+ /**
496
+ * Verify crawler nonce from scan id
497
+ */
498
+ protected static function verify_notify_nonce($value) {
499
+ return WPLNST_Core_Nonce::verify_nonce($value, 'crawl-notify-'.self::get_crawler_slug());
500
+ }
501
+
502
+
503
+
504
+ /**
505
+ * Create nonce for the notifier
506
+ */
507
+ public static function get_notify_nonce() {
508
+ return WPLNST_Core_Nonce::create_nonce('crawl-notify-'.self::get_crawler_slug());
509
+ }
510
+
511
+
512
+
513
+ /**
514
+ * Check pending notifications
515
+ */
516
+ public static function notify() {
517
+
518
+ // Load translations
519
+ WPLNST_Core_Plugin::load_plugin_textdomain();
520
+
521
+ // Include and call class
522
+ wplnst_require('core', 'notify');
523
+ WPLNST_Core_Notify::check();
524
+
525
+ // End
526
+ die;
527
+ }
528
+
529
+
530
+
531
+ }
core/boot.php ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * WP Link Status Core Boot check
5
+ *
6
+ * @package WP Link Status
7
+ * @subpackage WP Link Status Core
8
+ */
9
+
10
+ // Check class or constants conflict relative to other active plugins
11
+ if (class_exists('WPLNST_Core_Alive') || defined('WPLNST_VERSION') || defined('WPLNST_FILE') || defined('WPLNST_PATH')) {
12
+
13
+ // No execution allowed
14
+ trigger_error(__('Detected another version of WP Link Status already active. Please deactivate it before and try again to activate this plugin.', 'wplnst'), E_USER_ERROR);
15
+
16
+ // Check WP version via checking version expected function, because $wp_version may have been overwritten
17
+ } elseif (function_exists('add_action') && !function_exists('is_main_query')) {
18
+
19
+ // No compatible WP version
20
+ trigger_error(__('Sorry, this version of WP Link Status requires WordPress 3.3 or later.', 'wplnst'), E_USER_ERROR);
21
+ }
core/crawler.php ADDED
@@ -0,0 +1,2249 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Load main class
4
+ require_once(dirname(__FILE__).'/module.php');
5
+
6
+ /**
7
+ * WP Link Status Core Crawler class
8
+ *
9
+ * @package WP Link Status
10
+ * @subpackage WP Link Status Core
11
+ */
12
+ class WPLNST_Core_Crawler extends WPLNST_Core_Module {
13
+
14
+
15
+
16
+ // Properties
17
+ // ---------------------------------------------------------------------------------------------------
18
+
19
+
20
+
21
+ /**
22
+ * Current scan
23
+ */
24
+ private $scan;
25
+
26
+
27
+
28
+ /**
29
+ * Current thread
30
+ */
31
+ private $thread_id;
32
+
33
+
34
+
35
+ /**
36
+ * All timeouts
37
+ */
38
+ private $connect_timeout;
39
+ private $request_timeout;
40
+ private $total_timeout;
41
+
42
+
43
+
44
+ /**
45
+ * Current pack index
46
+ */
47
+ private $pack_index = 0;
48
+
49
+
50
+
51
+ /**
52
+ * Totals content
53
+ */
54
+ private $total_posts;
55
+ private $total_comments;
56
+ private $total_blogroll;
57
+
58
+
59
+
60
+ /**
61
+ * Back crawler URL
62
+ */
63
+ private $crawler_url;
64
+
65
+
66
+
67
+ /**
68
+ * Content args values
69
+ */
70
+ private $post_args;
71
+ private $comment_args;
72
+ private $blogroll_args;
73
+
74
+
75
+
76
+ // Initialization
77
+ // ---------------------------------------------------------------------------------------------------
78
+
79
+
80
+
81
+ /**
82
+ * Creates a singleton object
83
+ */
84
+ public static function instantiate($args = null) {
85
+ return self::get_instance(get_class(), $args);
86
+ }
87
+
88
+
89
+
90
+ /**
91
+ * Custom constructor
92
+ */
93
+ protected function on_construct($args = null) {
94
+
95
+ // Check scan argument
96
+ $scan_id = empty($args['scan_id'])? 0 : (int) $args['scan_id'];
97
+ if (empty($scan_id))
98
+ return;
99
+
100
+ // Check thread argument
101
+ $this->thread_id = empty($args['thread_id'])? 0 : (int) $args['thread_id'];
102
+ if (empty($this->thread_id))
103
+ return;
104
+
105
+ // Crawler URL argument
106
+ $this->crawler_url = empty($args['crawler_url'])? false : $args['crawler_url'];
107
+ if (empty($this->crawler_url))
108
+ return;
109
+
110
+ // Check scan record
111
+ if (false === ($this->scan = $this->get_scan_by_id($scan_id)))
112
+ return;
113
+
114
+ // Debug point
115
+ $this->debug('__construct');
116
+
117
+ // URL object
118
+ $this->load_url_object();
119
+
120
+ // All timeouts
121
+ $this->set_timeouts();
122
+
123
+ // Incoming POST request data
124
+ if (!empty($_POST['url_id']))
125
+ $this->request();
126
+
127
+ // Check waiting links before next content data
128
+ if (!$this->inspect())
129
+ $this->content();
130
+ }
131
+
132
+
133
+
134
+ // External request data update and quota check
135
+ // ---------------------------------------------------------------------------------------------------
136
+
137
+
138
+
139
+ /**
140
+ * Connect and request timeout
141
+ */
142
+ private function set_timeouts() {
143
+
144
+ // Local timeouts
145
+ $this->connect_timeout = wplnst_get_nsetting('connect_timeout', $this->scan->threads->connect_timeout);
146
+ $this->request_timeout = wplnst_get_nsetting('request_timeout', $this->scan->threads->request_timeout);
147
+
148
+ // Timeouts sum and extra
149
+ $this->total_timeout = $this->connect_timeout + $this->request_timeout + wplnst_get_nsetting('extra_timeout');
150
+ }
151
+
152
+
153
+
154
+ /**
155
+ * Result from a external request
156
+ */
157
+ private function request() {
158
+
159
+ // Debug point
160
+ $this->debug('request');
161
+
162
+ // Check URL id
163
+ $url_id = (int) $_POST['url_id'];
164
+ if (empty($url_id))
165
+ return;
166
+
167
+ // Check URL record
168
+ if (false === ($url = $this->scans->get_scan_url(array('id' => $url_id, 'no_cache' => true))))
169
+ return;
170
+
171
+ // Check URL status record
172
+ if (false === ($rows = $this->scans->get_scan_url_status(array('url_id' => $url_id, 'scan_id' => $this->scan->id, 'no_cache' => true))))
173
+ return;
174
+
175
+ // Check URL status phase
176
+ if (1 != count($rows) || in_array($rows[0]->phase, array('end', 'discard', 'failed')))
177
+ return;
178
+
179
+ // Check redirection mode
180
+ $redirection = (!empty($_GET['wplnst_redirection']) && '1' == $_GET['wplnst_redirection']);
181
+
182
+ // Analyze request
183
+ wplnst_require('core', 'status');
184
+ $status = new WPLNST_Core_Status($_POST);
185
+
186
+ // Check discarded response
187
+ $discard = empty($status->level)? false : (!in_array($status->level, $this->scan->status_levels) && !in_array($status->code, $this->scan->status_codes));
188
+
189
+ // Check redirection steps
190
+ $redirect_steps = @json_decode($rows[0]->redirect_steps, true);
191
+ if (empty($redirect_steps) || !is_array($redirect_steps))
192
+ $redirect_steps = array();
193
+
194
+ // Normal mode
195
+ if (!$this->scan->redir_status || !$redirection) {
196
+
197
+ // Check redirection
198
+ $phase_next = 'end';
199
+ $redirect_url_id = 0;
200
+ $redirect_url_status = "";
201
+ $redirect_curl_errno = 0;
202
+ if ($this->scan->redir_status && !$discard && 3 == $status->level && !empty($status->redirect_url)) {
203
+
204
+ // Default redirection
205
+ $redirect_url = $status->redirect_url;
206
+
207
+ // Analyze
208
+ $urlinfo = $this->urlo->parse($redirect_url, $url->url);
209
+ if ($this->urlo->is_crawleable($urlinfo)) {
210
+
211
+ // Copy final URL
212
+ $redirect_url = $urlinfo['url'];
213
+
214
+ // Search by main URL
215
+ if (false !== ($redir_url = $this->scans->get_scan_url(array('url' => $redirect_url, 'no_cache' => true)))) {
216
+
217
+ // Existing URL
218
+ $phase_next = 'redir';
219
+ $redirect_url_id = $redir_url->url_id;
220
+
221
+ // New URL for this scan
222
+ } elseif (false !== ($test_url_id = $this->scans->add_scan_url($urlinfo, $this->scan->id))) {
223
+
224
+ // Mark to check
225
+ $phase_next = 'redir';
226
+ $redirect_url_id = $test_url_id;
227
+ }
228
+ }
229
+
230
+ // Add possible fragment to save "As is"
231
+ $redirect_url .= $urlinfo['fragment'];
232
+ }
233
+
234
+ // Update status data
235
+ $this->scans->update_scan_url_status($url_id, $this->scan->id, array(
236
+ 'phase' => $discard? 'discard' : $phase_next,
237
+ 'request_at' => $status->request_at,
238
+ 'status_level' => $status->level,
239
+ 'status_code' => $status->code,
240
+ 'redirect_url' => $discard? '' : (isset($redirect_url)? $redirect_url : ''),
241
+ 'redirect_url_id' => $redirect_url_id,
242
+ 'redirect_url_status' => $redirect_url_status,
243
+ 'redirect_curl_errno' => $redirect_curl_errno,
244
+ 'headers' => $discard? '' : (empty($status->headers)? '' : @json_encode($status->headers)),
245
+ 'headers_request' => $discard? '' : (empty($status->headers_request)? '' : @json_encode($status->headers_request)),
246
+ 'total_time' => $discard? 0 : $status->total_time,
247
+ 'total_bytes' => $discard? 0 : $status->total_bytes,
248
+ 'curl_errno' => $discard? 0 : $status->curl_errno,
249
+ 'requests' => $discard? 0 : (int) $rows[0]->requests + 1,
250
+ ));
251
+
252
+ // Update URL data
253
+ $this->scans->update_scan_url($url_id, array(
254
+ 'last_scan_id' => $this->scan->id,
255
+ 'last_status_level' => $status->level,
256
+ 'last_status_code' => $status->code,
257
+ 'last_curl_errno' => $status->curl_errno,
258
+ 'last_request_at' => $status->request_at,
259
+ ));
260
+
261
+ // Is Redirection
262
+ } else {
263
+
264
+ // Prepare update
265
+ $update = array(
266
+ 'phase' => 'end',
267
+ 'redirect_url_status' => $status->code,
268
+ 'redirect_curl_errno' => $status->curl_errno,
269
+ 'requests' => (int) $rows[0]->requests + 1,
270
+ );
271
+
272
+ // Check new redirection
273
+ if (empty($curl_errno) && 3 == $status->level && !empty($status->redirect_url) && ($rows[0]->requests + 1) <= wplnst_get_nsetting('max_redirs')) {
274
+
275
+ // Check crawleable
276
+ $urlinfo = $this->urlo->parse($status->redirect_url, $url->url);
277
+ if ($this->urlo->is_crawleable($urlinfo)) {
278
+
279
+ // Search by main URL
280
+ if (false !== ($redir_url = $this->scans->get_scan_url(array('url' => $urlinfo['url'], 'no_cache' => true)))) {
281
+
282
+ // Existing URL
283
+ $redirect_url_id = $redir_url->url_id;
284
+
285
+ // New URL for this scan
286
+ } elseif (false !== ($test_url_id = $this->scans->add_scan_url($urlinfo, $this->scan->id))) {
287
+
288
+ // Added URL
289
+ $redirect_url_id = $test_url_id;
290
+ }
291
+
292
+ // For existing or correctly added URL
293
+ if (!empty($redirect_url_id)) {
294
+
295
+ // Copy old steps
296
+ $redirect_steps[] = array('url' => $rows[0]->redirect_url, 'status' => $rows[0]->redirect_url_status);
297
+
298
+ // New redir
299
+ $update['phase'] = 'redir';
300
+ $update['redirect_url'] = $urlinfo['url'].$urlinfo['fragment'];
301
+ $update['redirect_url_id'] = $redirect_url_id;
302
+ $update['redirect_url_status'] = "";
303
+ $update['redirect_steps'] = @json_encode($redirection_steps);
304
+ }
305
+ }
306
+ }
307
+
308
+ // Update status data
309
+ $this->scans->update_scan_url_status($url_id, $this->scan->id, $update);
310
+ }
311
+
312
+ // Status codes
313
+ $this->scans->set_scan_summary_status_codes($this->scan->id, $this->scan->status_levels, $this->scan->status_codes);
314
+
315
+ // URLs summary
316
+ $this->scans->set_scan_summary_urls_phases($this->scan->id);
317
+ }
318
+
319
+
320
+
321
+ // Stored links inspection
322
+ // ---------------------------------------------------------------------------------------------------
323
+
324
+
325
+
326
+ /**
327
+ * Launch new URL`s status checks
328
+ */
329
+ private function inspect() {
330
+
331
+ // Debug point
332
+ $this->debug('inspect');
333
+
334
+ // Check if this scan is playing
335
+ if ('play' != $this->scans->get_scan_status($this->scan->id)) {
336
+
337
+ // Stopped or ended
338
+ return true;
339
+
340
+ // Retrieve next URL
341
+ } elseif (false !== ($url = $this->inspect_wait())) {
342
+
343
+ // Check no playing rows flag, and update started datetime
344
+ if (true !== $url && false !== $this->scans->update_scan_url_status($url->url_id, $this->scan->id, array('phase' => 'play', 'started_at' => current_time('mysql', true)))) {
345
+
346
+ // Debug point
347
+ $this->debug('inspect request');
348
+
349
+ // Check Crawler URL in redirection mode
350
+ $crawler_url = $this->crawler_url;
351
+ if (isset($url->redirection))
352
+ $crawler_url = add_query_arg('wplnst_redirection', '1', $crawler_url);
353
+
354
+ // Prepare POST fields
355
+ $postfields = array(
356
+ 'url' => $url->url,
357
+ 'hash' => $url->hash,
358
+ 'url_id' => $url->url_id,
359
+ 'connect_timeout' => $this->connect_timeout,
360
+ 'request_timeout' => $this->request_timeout,
361
+ 'max_download' => wplnst_get_nsetting('max_download') * 1024,
362
+ 'back_url' => $crawler_url,
363
+ 'debug' => wplnst_is_debug()? '1' : '0',
364
+ 'user_agent' => wplnst_get_tsetting('user_agent'),
365
+ 'nonce' => WPLNST_Core_Nonce::create_nonce($url->hash),
366
+ );
367
+
368
+ // Load cURL wrapper library
369
+ wplnst_require('core', 'curl');
370
+
371
+ // Spawn crawler call
372
+ WPLNST_Core_CURL::spawn(array(
373
+ 'CURLOPT_URL' => plugins_url('core/requests/http.php', WPLNST_FILE),
374
+ 'CURLOPT_USERAGENT' => wplnst_get_tsetting('user_agent'),
375
+ 'CURLOPT_POST' => true,
376
+ 'CURLOPT_POSTFIELDS' => http_build_query($postfields, null, '&'),
377
+ 'CURLOPT_HTTPHEADER' => array('Content-Type: application/x-www-form-urlencoded; charset=utf-8'),
378
+ ));
379
+
380
+ // Ends here, it's going out to check status
381
+ return true;
382
+ }
383
+
384
+ // Check scan ending from all content
385
+ } elseif ($this->content_end()) {
386
+
387
+ // No timeout
388
+ set_time_limit(0);
389
+
390
+ // Set "end" scan status
391
+ $this->scans->end_scan($this->scan->id);
392
+
393
+ // URLs status codes summary
394
+ $this->scans->set_scan_summary_status_codes($this->scan->id, $this->scan->status_levels, $this->scan->status_codes, true);
395
+
396
+ // URLs phases summary
397
+ $this->scans->set_scan_summary_urls_phases($this->scan->id, true);
398
+
399
+ // Update posts matched
400
+ if ($this->scan->check_posts)
401
+ $this->scans->set_scan_summary_objects_match($this->scan->id, 'posts', true);
402
+
403
+ // Update posts matched
404
+ if ($this->scan->check_comments)
405
+ $this->scans->set_scan_summary_objects_match($this->scan->id, 'comments', true);
406
+
407
+ // Update blogroll matched
408
+ if ($this->scan->check_blogroll)
409
+ $this->scans->set_scan_summary_objects_match($this->scan->id, 'blogroll', true);
410
+
411
+ // Remove discarded URLS
412
+ $this->scans->remove_scan_discard_urls($this->scan->id);
413
+
414
+ // Update real total posts
415
+ if ($this->scan->check_posts)
416
+ $this->scans->update_scan_trace($this->scan->id, array('total_posts' => $this->scans->get_scan_objects_count($this->scan->id, 'posts')));
417
+
418
+ // Remove registered objects
419
+ $this->scans->remove_scan_objects($this->scan->id);
420
+
421
+ // Check notifications
422
+ if ($this->scan->notify_default || ($this->scan->notify_address && !empty($this->scan->notify_address_email))) {
423
+ wplnst_require('core', 'notify');
424
+ WPLNST_Core_Notify::completed($this->scan);
425
+ }
426
+
427
+ // Check for queued scans
428
+ $this->activity();
429
+
430
+ // The end
431
+ return true;
432
+ }
433
+
434
+ // No wait or play with timeout
435
+ return false;
436
+ }
437
+
438
+
439
+
440
+ /**
441
+ * Inspect a waiting url to check
442
+ */
443
+ private function inspect_wait() {
444
+
445
+ // Retrieve next wait
446
+ if (false !== ($url = $this->scans->get_scan_url_waiting($this->scan->id)))
447
+ return $url;
448
+
449
+ // Check playing rows
450
+ if (false === ($rows = $this->scans->get_scan_url_status(array('scan_id' => $this->scan->id, 'phase' => 'play', 'no_cache' => true, 'order' => 'started_at ASC'))))
451
+ return false;
452
+
453
+ // Max requests allowed
454
+ $max_requests = wplnst_get_nsetting('max_requests');
455
+
456
+ // Enum playing rows
457
+ foreach ($rows as $row) {
458
+
459
+ // Check timeouted
460
+ if (time() - strtotime($row->started_at.' UTC') > $this->total_timeout) {
461
+
462
+ // Check max requests
463
+ if ($row->requests >= $max_requests) {
464
+
465
+ // Mark as a request phase
466
+ $this->scans->update_scan_url_status($url->url_id, $this->scan->id, array('phase' => 'failed'));
467
+
468
+ // Retrieve URL
469
+ } elseif (false !== ($url = $this->scans->get_scan_url(array('id' => $row->url_id, 'no_cache' => true)))) {
470
+
471
+ // Update from play to wait
472
+ if (false !== $this->scans->update_scan_url_status($url->url_id, $this->scan->id, array('phase' => 'wait'))) {
473
+
474
+ // URLs summary
475
+ $this->scans->set_scan_summary_urls_phases($this->scan->id);
476
+
477
+ // Done
478
+ return $url;
479
+ }
480
+ }
481
+ }
482
+ }
483
+
484
+ // Playing rows
485
+ return false;
486
+ }
487
+
488
+
489
+
490
+ // Content data extraction procedures
491
+ // ---------------------------------------------------------------------------------------------------
492
+
493
+
494
+
495
+ /**
496
+ * Initialize post args
497
+ */
498
+ private function set_post_args() {
499
+
500
+ // Check previous
501
+ if (isset($this->post_args))
502
+ return;
503
+
504
+ // Initialize arguments
505
+ $this->post_args = array();
506
+
507
+ // Prepare types and status fragments
508
+ $this->post_args['post_types'] = (1 == count($this->scan->post_types))? '= "'.esc_sql($this->scan->post_types[0]).'"' : 'IN ("'.implode('", "', array_map('esc_sql', $this->scan->post_types)).'")';
509
+ $this->post_args['post_status'] = (1 == count($this->scan->post_status))? '= "'.esc_sql($this->scan->post_status[0]).'"' : 'IN ("'.implode('", "', array_map('esc_sql', $this->scan->post_status)).'")';
510
+
511
+ // Time scope argument
512
+ $this->post_args['time_scope'] = $this->get_time_scope_arg('post_date_gmt');
513
+
514
+ // Check order
515
+ $this->post_args['order_by'] = 'post_date_gmt '.(('asc' == $this->scan->crawl_order)? 'ASC' : 'DESC');
516
+
517
+ // Filters arguments
518
+ $this->post_args['content_filters'] = $this->get_content_filters_arg('post_content');
519
+ }
520
+
521
+
522
+
523
+ /**
524
+ * Initialize comments args
525
+ */
526
+ private function set_comment_args() {
527
+
528
+ // Check previous
529
+ if (isset($this->comment_args))
530
+ return;
531
+
532
+ // Initialize
533
+ $this->comment_args = array();
534
+
535
+ // Comment status
536
+ $comment_types = WPLNST_Core_Types::get_comment_types_values($this->scan->comment_types);
537
+ $this->comment_args['comment_status'] = ' AND comment_approved '.((1 == count($comment_types))? '= "'.esc_sql($comment_types[0]).'"' : 'IN ("'.implode('", "', array_map('esc_sql', $comment_types)).'")');
538
+
539
+ // Time scope argument
540
+ $this->comment_args['time_scope'] = $this->get_time_scope_arg('comment_date_gmt');
541
+
542
+ // Filters arguments
543
+ $this->comment_args['content_filters'] = $this->get_content_filters_arg('comment_content');
544
+
545
+ // Check order
546
+ $this->comment_args['order_by'] = 'comment_date_gmt '.(('asc' == $this->scan->crawl_order)? 'ASC' : 'DESC');
547
+ }
548
+
549
+
550
+
551
+ /**
552
+ * Initialize blogroll args
553
+ */
554
+ private function set_blogroll_args() {
555
+
556
+ // Check previous
557
+ if (isset($this->blogroll_args))
558
+ return;
559
+
560
+ // Initialize arguments
561
+ $this->blogroll_args = array();
562
+
563
+ // Check order
564
+ $this->blogroll_args['order_by'] = 'link_id '.(('asc' == $this->scan->crawl_order)? 'ASC' : 'DESC');
565
+ }
566
+
567
+
568
+
569
+ /**
570
+ * Process next post
571
+ */
572
+ private function content() {
573
+
574
+ // Debug point
575
+ $this->debug('content');
576
+
577
+ // No script timeout
578
+ set_time_limit(0);
579
+
580
+ // Retrieve trace array
581
+ $trace = $this->scans->get_scan_trace($this->scan->id);
582
+
583
+ // Initialize process flags
584
+ $process_posts = $process_comments = $process_blogroll = false;
585
+
586
+
587
+ /* Check content */
588
+
589
+ // Check posts
590
+ if ($this->scan->check_posts && empty($trace['populated_posts'])) {
591
+ $process_posts = true;
592
+ $this->content_total('posts');
593
+ }
594
+
595
+ // Check comments
596
+ if ($this->scan->check_comments && empty($trace['populated_comments'])) {
597
+ $process_comments = true;
598
+ if (empty($trace['total_comments_check']) || !$process_posts)
599
+ $this->content_total('comments');
600
+ }
601
+
602
+ // Check blogroll
603
+ if ($this->scan->check_blogroll && empty($trace['populated_blogroll'])) {
604
+
605
+ // Check links arg, if not update trace
606
+ if (!in_array('links', $this->scan->link_types)) {
607
+ $this->total_blogroll = 0;
608
+ $this->scans->update_scan_trace($this->scan->id, array('populated_blogroll' => true, 'total_blogroll' => 0));
609
+
610
+ // Continue
611
+ } else {
612
+
613
+ // Mark for process
614
+ $process_blogroll = true;
615
+
616
+ // Check first update
617
+ if (empty($trace['total_blogroll_check']) || (!$process_posts && !$process_comments))
618
+ $this->content_total('blogroll');
619
+ }
620
+ }
621
+
622
+
623
+ // Common filters
624
+ add_filter('wplnst_relative_parent_url', array(&$this, 'parent_permalink'));
625
+
626
+
627
+ /* Processing objects */
628
+
629
+ // Process posts
630
+ if ($process_posts && !$this->content_posts())
631
+ return;
632
+
633
+ // Process comments
634
+ if ($process_comments && !$this->content_comments())
635
+ return;
636
+
637
+ // Process blogroll
638
+ if ($process_blogroll && !$this->content_blogroll())
639
+ return;
640
+
641
+
642
+ /* External work or run again */
643
+
644
+ // More data or restart
645
+ if (!$this->inspect())
646
+ $this->restart();
647
+ }
648
+
649
+
650
+
651
+ /**
652
+ * Check if this is the end of all content types
653
+ */
654
+ private function content_end() {
655
+
656
+ // Initialize
657
+ $the_end = true;
658
+
659
+ // Check posts
660
+ if ($this->scan->check_posts)
661
+ $the_end = (false !== $this->scans->get_scan_trace($this->scan->id, 'populated_posts'));
662
+
663
+ // Check comments
664
+ if ($the_end && $this->scan->check_comments)
665
+ $the_end = (false !== $this->scans->get_scan_trace($this->scan->id, 'populated_comments'));
666
+
667
+ // Check blogroll
668
+ if ($the_end && $this->scan->check_blogroll)
669
+ $the_end = (false !== $this->scans->get_scan_trace($this->scan->id, 'populated_blogroll'));
670
+
671
+ // Done
672
+ return $the_end;
673
+ }
674
+
675
+
676
+
677
+ /**
678
+ * Time scope limits
679
+ */
680
+ private function get_time_scope_arg($field) {
681
+
682
+ // First basic check
683
+ if (empty($this->scan->time_scope) || 'anytime' == $this->scan->time_scope)
684
+ return '';
685
+
686
+ // Check real value
687
+ if (!in_array($this->scan->time_scope, array_keys(WPLNST_Core_Types::get_time_scopes())))
688
+ return '';
689
+
690
+ // Yesterday
691
+ if ('yesterday' == $this->scan->time_scope) {
692
+ $back_date = ' > "'.gmdate('Y-m-d 00:00:00', strtotime("-1 days")).'"';
693
+
694
+ // From one week ago
695
+ } elseif ('7days' == $this->scan->time_scope) {
696
+ $back_date = ' > "'.gmdate('Y-m-d 00:00:00', strtotime("-7 days")).'"';
697
+
698
+ // From 15 days ago
699
+ } elseif ('15days' == $this->scan->time_scope) {
700
+ $back_date = ' > "'.gmdate('Y-m-d 00:00:00', strtotime("-15 days")).'"';
701
+
702
+ // From 1 month ago
703
+ } elseif ('month' == $this->scan->time_scope) {
704
+ $back_date = ' > "'.gmdate('Y-m-d 00:00:00', strtotime("-1 months")).'"';
705
+
706
+ // From 3 months ago
707
+ } elseif ('3months' == $this->scan->time_scope) {
708
+ $back_date = ' > "'.gmdate('Y-m-d 00:00:00', strtotime("-3 months")).'"';
709
+
710
+ } elseif ('6months' == $this->scan->time_scope) {
711
+ $back_date = ' > "'.gmdate('Y-m-d 00:00:00', strtotime("-6 months")).'"';
712
+
713
+ } elseif ('year' == $this->scan->time_scope) {
714
+ $back_date = ' > "'.gmdate('Y-m-d 00:00:00', strtotime("-1 years")).'"';
715
+
716
+ } elseif ('custom' == $this->scan->time_scope) {
717
+ // Pending
718
+ }
719
+
720
+ // Check value and compose subquery
721
+ return isset($back_date)? ' AND '.$field.$back_date : '';
722
+ }
723
+
724
+
725
+
726
+ /**
727
+ * Content filters in query
728
+ */
729
+ private function get_content_filters_arg($field, $op = ' AND ') {
730
+
731
+ // Content matches
732
+ $matches = $this->filter_for_queries();
733
+ if (empty($matches))
734
+ return '';
735
+
736
+ // Prepare likes
737
+ $likes = array();
738
+ foreach ($matches as $match)
739
+ $likes[] = $field.' LIKE "%'.addcslashes($match, '_%\\').'%"';
740
+
741
+ // Likes fragment
742
+ return $op.((1 == count($likes))? $likes[0] : '('.implode(' OR ', $likes).')');
743
+ }
744
+
745
+
746
+
747
+ /**
748
+ * Update total posts
749
+ */
750
+ private function content_total($type, $final = false) {
751
+
752
+ // Globals
753
+ global $wpdb;
754
+
755
+ // Retrieve trace array
756
+ $trace = $this->scans->get_scan_trace($this->scan->id);
757
+
758
+ // Update posts
759
+ if ('posts' == $type) {
760
+
761
+ // Debug point
762
+ $this->debug('content_total posts');
763
+
764
+ // Check trace value
765
+ $total_posts = empty($trace['total_posts'])? 0 : (int) $trace['total_posts'];
766
+
767
+ // Total posts check
768
+ $timestamp = empty($trace['total_posts_check'])? false : (int) $trace['total_posts_check'];
769
+ if (false === $timestamp || (time() - $timestamp) >= wplnst_get_nsetting('total_objects')) {
770
+
771
+ // Debug point
772
+ $this->debug('content_total posts update');
773
+
774
+ // Mark checked time
775
+ $this->scans->update_scan_trace($this->scan->id, array('total_posts_check' => time()));
776
+
777
+ // Set filters
778
+ $this->set_post_args();
779
+
780
+ // Prepare query
781
+ $sql = 'SELECT COUNT(*) FROM '.$wpdb->posts.' WHERE post_type '.$this->post_args['post_types'].' AND post_status '.$this->post_args['post_status'].$this->post_args['time_scope'].$this->post_args['content_filters'];
782
+
783
+ // Check last date condition
784
+ $last_date_gmt = $this->scans->get_scan_trace($this->scan->id, 'last_post_date_gmt');
785
+ if (empty($last_date_gmt)) {
786
+
787
+ // Total posts without restrictions
788
+ $total_posts = (int) $wpdb->get_var($sql);
789
+
790
+ // Check
791
+ } else {
792
+
793
+ // Sum processed and no-processed
794
+ $where = ' AND post_date_gmt '.(('asc' == $this->scan->crawl_order)? '>=' : '<=').' "'.esc_sql($last_date_gmt).'"';
795
+ $total_posts = (int) $this->scans->get_scan_trace($this->scan->id, 'posts_index') + (int) $wpdb->get_var($sql.$where.' AND ID NOT IN (SELECT object_id FROM '.$wpdb->prefix.'wplnst_scans_objects WHERE scan_id = '.esc_sql($this->scan->id).' AND object_type = "posts")');
796
+ }
797
+
798
+ // Update total posts
799
+ $this->scans->update_scan_trace($this->scan->id, array('total_posts' => $total_posts));
800
+ }
801
+
802
+ // Assign results
803
+ $this->total_posts = $total_posts;
804
+
805
+ // Update total comments
806
+ } elseif ('comments' == $type) {
807
+
808
+ // Debug point
809
+ $this->debug('content_total comments');
810
+
811
+ // Check trace value
812
+ $total_comments = empty($trace['total_comments'])? 0 : (int) $trace['total_comments'];
813
+
814
+ // Total comments check
815
+ $timestamp = empty($trace['total_comments_check'])? false : (int) $trace['total_comments_check'];
816
+ if (false === $timestamp || (time() - $timestamp) >= wplnst_get_nsetting('total_objects')) {
817
+
818
+ // Debug point
819
+ $this->debug('content_total comments update');
820
+
821
+ // Mark checked time
822
+ $this->scans->update_scan_trace($this->scan->id, array('total_comments_check' => time()));
823
+
824
+ // Set filters
825
+ $this->set_comment_args();
826
+
827
+ // Prepare query
828
+ $sql = 'SELECT COUNT(*) FROM '.$wpdb->comments.' WHERE 1 = 1 '.$this->comment_args['comment_status'].$this->comment_args['time_scope'].$this->comment_args['content_filters'];
829
+
830
+ // Check last date condition
831
+ $last_date_gmt = $this->scans->get_scan_trace($this->scan->id, 'last_comment_date_gmt');
832
+ if (empty($last_date_gmt)) {
833
+
834
+ // Total comments without restrictions
835
+ $total_comments = (int) $wpdb->get_var($sql);
836
+
837
+ // Check
838
+ } else {
839
+
840
+ // Sum processed and no-processed
841
+ $where = ' AND comment_date_gmt '.(('asc' == $this->scan->crawl_order)? '>=' : '<=').' "'.esc_sql($last_date_gmt).'"';
842
+ $total_comments = (int) $this->scans->get_scan_trace($this->scan->id, 'comments_index') + (int) $wpdb->get_var($sql.$where.' AND comment_ID NOT IN (SELECT object_id FROM '.$wpdb->prefix.'wplnst_scans_objects WHERE scan_id = '.esc_sql($this->scan->id).' AND object_type = "comments")');
843
+ }
844
+
845
+ // Update total comments
846
+ $this->scans->update_scan_trace($this->scan->id, array('total_comments' => $total_comments));
847
+ }
848
+
849
+ // Assign results
850
+ $this->total_comments = $total_comments;
851
+
852
+ // Update total blogroll links
853
+ } elseif ('blogroll' == $type) {
854
+
855
+ // Debug point
856
+ $this->debug('content_total blogroll');
857
+
858
+ // Check trace value
859
+ $total_blogroll = empty($trace['total_blogroll'])? 0 : (int) $trace['total_blogroll'];
860
+
861
+ // Total blogroll check
862
+ $timestamp = empty($trace['total_blogroll_check'])? false : (int) $trace['total_blogroll_check'];
863
+ if (false === $timestamp || (time() - $timestamp) >= wplnst_get_nsetting('total_objects')) {
864
+
865
+ // Debug point
866
+ $this->debug('content_total blogroll update');
867
+
868
+ // Mark checked time
869
+ $this->scans->update_scan_trace($this->scan->id, array('total_blogroll_check' => time()));
870
+
871
+ // Set filters
872
+ $this->set_blogroll_args();
873
+
874
+ // Prepare query
875
+ $sql = 'SELECT COUNT(*) FROM '.$wpdb->links;
876
+
877
+ // Check last identifier condition
878
+ $last_id = (int) $this->scans->get_scan_trace($this->scan->id, 'last_blogroll_id');
879
+ if (empty($last_id)) {
880
+
881
+ // Total blogroll without restrictions
882
+ $total_blogroll = (int) $wpdb->get_var($sql);
883
+
884
+ // Check
885
+ } else {
886
+
887
+ // Sum processed and no-processed
888
+ $where = ' WHERE link_id '.(('asc' == $this->scan->crawl_order)? '>' : '<').' '.$last_id;
889
+ $total_blogroll = (int) $this->scans->get_scan_trace($this->scan->id, 'blogroll_index') + (int) $wpdb->get_var($sql.$where.' AND link_id NOT IN (SELECT object_id FROM '.$wpdb->prefix.'wplnst_scans_objects WHERE scan_id = '.esc_sql($this->scan->id).' AND object_type = "blogroll")');
890
+ }
891
+
892
+ // Update total blogroll
893
+ $this->scans->update_scan_trace($this->scan->id, array('total_blogroll' => $total_blogroll));
894
+ }
895
+
896
+ // Assign results
897
+ $this->total_blogroll = $total_blogroll;
898
+ }
899
+ }
900
+
901
+
902
+
903
+ /**
904
+ * Obtains and process contents from posts
905
+ */
906
+ private function content_posts() {
907
+
908
+ // Recursion limit
909
+ static $recursion;
910
+ $recursion = isset($recursion)? 1 : $recursion + 1;
911
+ if ($recursion >= wplnst_get_nsetting('recursion_limit')) {
912
+ $this->restart();
913
+ return false;
914
+ }
915
+
916
+ // Content max pack
917
+ $this->pack_index++;
918
+ if ($this->pack_index > wplnst_get_nsetting('max_pack')) {
919
+ $this->restart();
920
+ return false;
921
+ }
922
+
923
+ // Check next post
924
+ if (false !== ($post = $this->content_posts_next())) {
925
+
926
+ // Register object
927
+ if (!$this->scans->register_scan_object($this->scan->id, $post->ID, 'posts', $post->post_date_gmt))
928
+ return $this->content_posts();
929
+
930
+ // Update last date gmt
931
+ $this->scans->update_scan_trace($this->scan->id, array('last_post_date_gmt' => $post->post_date_gmt));
932
+
933
+ // Update posts index
934
+ $this->scans->update_scan_trace($this->scan->id, array('posts_index' => $this->scans->get_scan_objects_count($this->scan->id, 'posts')));
935
+
936
+ // Initialize
937
+ $links_added = array();
938
+ $images_added = array();
939
+ $custom_fields_links_added = array();
940
+ $custom_fields_images_added = array();
941
+
942
+ // Parent permalink reference
943
+ $parent_url = array('post_id' => $post->ID);
944
+
945
+ // Process content
946
+ if (!empty($post->post_content)) {
947
+
948
+ // Process links
949
+ if (in_array('links', $this->scan->link_types)) {
950
+ $links = $this->extract_links($post->post_content, $parent_url, $post->ID, 'posts', $post->post_type, 'post_content', $post->post_date_gmt);
951
+ if (!empty($links))
952
+ $links_added = $this->save($links, $post->ID);
953
+ }
954
+
955
+ // Process images
956
+ if (in_array('images', $this->scan->link_types)) {
957
+ $images = $this->extract_images($post->post_content, $parent_url, $post->ID, 'posts', $post->post_type, 'post_content', $post->post_date_gmt);
958
+ if (!empty($images))
959
+ $images_added = $this->save($images, $post->ID);
960
+ }
961
+ }
962
+
963
+ // Check custom fields
964
+ if (!empty($this->scan->custom_fields)) {
965
+
966
+ // Retrieve post custom fields
967
+ $post_metas = $this->scans->get_post_metas($post->ID);
968
+ if (!empty($post_metas) && is_array($post_metas)) {
969
+
970
+ // First compare field by key
971
+ foreach ($this->scan->custom_fields as $field) {
972
+
973
+ // Check field
974
+ if (empty($field['type']) || !isset($field['name']))
975
+ continue;
976
+
977
+ // Check existing name
978
+ $name = $field['name'];
979
+ if (isset($post_metas[$name])) {
980
+
981
+ // Check custom field content
982
+ $post_meta = $post_metas[$name];
983
+ if (!empty($post_meta) && is_array($post_meta)) {
984
+
985
+ // Enum values
986
+ foreach ($post_meta as $meta_id => $value) {
987
+
988
+ // Sanitize
989
+ $value = trim($value);
990
+ if (empty($value))
991
+ continue;
992
+
993
+ // Direct URL
994
+ if ('url' == $field['type']) {
995
+
996
+ // Check URL
997
+ $urlinfo = $this->urlo->parse($value, $parent_url);
998
+ if ($this->filter_include_urls($urlinfo['url']) && $this->filter_exclude_urls($urlinfo['url'])) {
999
+
1000
+ // Create link array
1001
+ $link = array_merge($urlinfo, array(
1002
+ 'anchor' => '',
1003
+ 'link_type' => 'links',
1004
+ 'object_id' => $post->ID,
1005
+ 'object_type' => 'posts',
1006
+ 'object_post_type' => $post->post_type,
1007
+ 'object_field' => 'custom_field_url_'.$meta_id.'_'.$name,
1008
+ 'object_date_gmt' => $post->post_date_gmt,
1009
+ ));
1010
+
1011
+ // Author links
1012
+ $custom_fields_links_added = $this->save(array($link), $post->ID);
1013
+ }
1014
+
1015
+ // HTML
1016
+ } else {
1017
+
1018
+ // Process links
1019
+ if (in_array('links', $this->scan->link_types)) {
1020
+ $links = $this->extract_links($value, $parent_url, $post->ID, 'posts', $post->post_type, 'custom_field_html_'.$meta_id.'_'.$name, $post->post_date_gmt);
1021
+ if (!empty($links))
1022
+ $custom_fields_links_added = $this->save($links, $post->ID);
1023
+ }
1024
+
1025
+ // Process images
1026
+ if (in_array('images', $this->scan->link_types)) {
1027
+ $images = $this->extract_images($value, $parent_url, $post->ID, 'posts', $post->post_type, 'custom_field_html_'.$meta_id.'_'.$name, $post->post_date_gmt);
1028
+ if (!empty($images))
1029
+ $custom_fields_images_added = $this->save($images, $post->ID);
1030
+ }
1031
+ }
1032
+ }
1033
+ }
1034
+ }
1035
+ }
1036
+ }
1037
+ }
1038
+
1039
+ // Update stats for summary
1040
+ if (!(empty($links_added) && empty($images_added) && empty($custom_fields_links_added) && empty($custom_fields_images_added))) {
1041
+
1042
+ // Update URLs phases
1043
+ $this->scans->set_scan_summary_urls_phases($this->scan->id);
1044
+
1045
+ // Update objects matched
1046
+ $this->scans->set_scan_summary_objects_match($this->scan->id, 'posts');
1047
+
1048
+ // Totals of content for each URL status added
1049
+ $added = array_unique(array_merge($links_added, $images_added, $custom_fields_links_added, $custom_fields_images_added));
1050
+ foreach ($added as $url_id)
1051
+ $this->scans->set_scan_url_status_total_content($url_id, $this->scan->id);
1052
+
1053
+ // Check inspect data
1054
+ if ($this->inspect())
1055
+ return false;
1056
+ }
1057
+
1058
+ // Empty
1059
+ } elseif ($this->scans->get_scan_objects_count($this->scan->id, 'posts') >= $this->total_posts) {
1060
+
1061
+ // Update populated status
1062
+ $this->scans->update_scan_trace($this->scan->id, array('populated_posts' => true));
1063
+
1064
+ // Check inspect data
1065
+ if ($this->inspect())
1066
+ return false;
1067
+
1068
+ // Check data to inspect
1069
+ } elseif ($this->inspect()) {
1070
+
1071
+ // Working
1072
+ return false;
1073
+ }
1074
+
1075
+ // End or next post
1076
+ return $this->scans->get_scan_trace($this->scan->id, 'populated_posts')? true : $this->content_posts();
1077
+ }
1078
+
1079
+
1080
+
1081
+ /**
1082
+ * Retrieve next post
1083
+ */
1084
+ private function content_posts_next($exceptions = array()) {
1085
+
1086
+ // Globals
1087
+ global $wpdb;
1088
+
1089
+ // Initialize
1090
+ $where = '1 = 1';
1091
+
1092
+ // Check exceptions
1093
+ if (!empty($exceptions))
1094
+ $where .= ' AND ID NOT IN ('.implode(',', array_map('intval', $exceptions)).')';
1095
+
1096
+ // Check last date condition
1097
+ $last_date_gmt = $this->scans->get_scan_trace($this->scan->id, 'last_post_date_gmt');
1098
+ if (!empty($last_date_gmt))
1099
+ $where .= ' AND post_date_gmt '.(('asc' == $this->scan->crawl_order)? '>=' : '<=').' "'.esc_sql($last_date_gmt).'"';
1100
+
1101
+ // Set arguments
1102
+ $this->set_post_args();
1103
+
1104
+ // Retrieve next post
1105
+ $post = $wpdb->get_row('SELECT ID, post_content, post_date_gmt, post_type FROM '.$wpdb->posts.' WHERE '.$where.' AND post_type '.$this->post_args['post_types'].' AND post_status '.$this->post_args['post_status'].$this->post_args['time_scope'].$this->post_args['content_filters'].' ORDER BY '.$this->post_args['order_by'].' LIMIT 1');
1106
+
1107
+ // Check if post is previously registered
1108
+ if (!empty($post) && $this->scans->scan_object_exists($this->scan->id, $post->ID, 'posts')) {
1109
+
1110
+ // Prepare identifiers
1111
+ $ids = array($post->ID);
1112
+
1113
+ // Check same value of last date
1114
+ if ($post->post_date_gmt == $last_date_gmt) {
1115
+
1116
+ // Check more identifiers with same date
1117
+ $existing_ids = $this->scans->get_scan_objects_ids_by_date($this->scan->id, 'posts', $last_date_gmt);
1118
+ if (!empty($existing_ids) && is_array($existing_ids))
1119
+ $ids = array_unique(array_merge($ids, $existing_ids));
1120
+ }
1121
+
1122
+ // Add exceptions
1123
+ $exceptions = empty($exceptions)? $ids : array_unique(array_merge($exceptions, $ids));
1124
+
1125
+ // Next post with exceptions
1126
+ $post = $this->content_posts_next($exceptions);
1127
+ }
1128
+
1129
+ // Done
1130
+ return empty($post)? false : $post;
1131
+ }
1132
+
1133
+
1134
+
1135
+ /**
1136
+ * Obtains and process contents from comments
1137
+ */
1138
+ private function content_comments() {
1139
+
1140
+ // Recursion limit
1141
+ static $recursion;
1142
+ $recursion = isset($recursion)? 1 : $recursion + 1;
1143
+ if ($recursion >= wplnst_get_nsetting('recursion_limit')) {
1144
+ $this->restart();
1145
+ return false;
1146
+ }
1147
+
1148
+ // Content max pack
1149
+ $this->pack_index++;
1150
+ if ($this->pack_index > wplnst_get_nsetting('max_pack')) {
1151
+ $this->restart();
1152
+ return false;
1153
+ }
1154
+
1155
+ // Check next comment
1156
+ if (false !== ($comment = $this->content_comments_next())) {
1157
+
1158
+ // Register object
1159
+ if (!$this->scans->register_scan_object($this->scan->id, $comment->comment_ID, 'comments', $comment->comment_date_gmt))
1160
+ return $this->content_comments();
1161
+
1162
+ // Update last date gmt
1163
+ $this->scans->update_scan_trace($this->scan->id, array('last_comment_date_gmt' => $comment->comment_date_gmt));
1164
+
1165
+ // Update comments index
1166
+ $this->scans->update_scan_trace($this->scan->id, array('comments_index' => $this->scans->get_scan_objects_count($this->scan->id, 'comments')));
1167
+
1168
+ // Initialize
1169
+ $links_added = array();
1170
+ $images_added = array();
1171
+ $authors_added = array();
1172
+
1173
+ // Retrieve parent permalink
1174
+ $parent_url = array('post_id' => $comment->comment_post_ID);
1175
+
1176
+ // Process content
1177
+ if (!empty($comment->comment_content)) {
1178
+
1179
+ // Process links
1180
+ if (in_array('links', $this->scan->link_types)) {
1181
+ $links = $this->extract_links($comment->comment_content, $parent_url, $comment->comment_ID, 'comments', "", 'comment_content', $comment->comment_date_gmt);
1182
+ if (!empty($links))
1183
+ $links_added = $this->save($links, $comment->comment_post_ID);
1184
+ }
1185
+
1186
+ // Process images
1187
+ if (in_array('images', $this->scan->link_types)) {
1188
+ $images = $this->extract_images($comment->comment_content, $parent_url, $comment->comment_ID, 'comments', "", 'comment_content', $comment->comment_date_gmt);
1189
+ if (!empty($images))
1190
+ $images_added = $this->save($images, $comment->comment_post_ID);
1191
+ }
1192
+ }
1193
+
1194
+ // Process author URL
1195
+ if (!empty($comment->comment_author_url) && in_array('links', $this->scan->link_types)) {
1196
+
1197
+ // Check URL
1198
+ $urlinfo = $this->urlo->parse($comment->comment_author_url, $parent_url);
1199
+ if ($this->filter_include_urls($urlinfo['url']) && $this->filter_exclude_urls($urlinfo['url'])) {
1200
+
1201
+ // Check anchor
1202
+ if ($this->filter_anchor_text($comment->comment_author)) {
1203
+
1204
+ // Create link array
1205
+ $link = array_merge($urlinfo, array(
1206
+ 'anchor' => $comment->comment_author,
1207
+ 'link_type' => 'links',
1208
+ 'object_id' => $comment->comment_ID,
1209
+ 'object_type' => 'comments',
1210
+ 'object_post_type' => "",
1211
+ 'object_field' => 'comment_author_url',
1212
+ 'object_date_gmt' => $comment->comment_date_gmt,
1213
+ ));
1214
+
1215
+ // Author links
1216
+ $authors_added = $this->save(array($link), $comment->comment_post_ID);
1217
+ }
1218
+ }
1219
+ }
1220
+
1221
+ // Update stats for summary
1222
+ if (!empty($links_added) || !empty($images_added) || !empty($authors_added)) {
1223
+
1224
+ // Update URLs phases
1225
+ $this->scans->set_scan_summary_urls_phases($this->scan->id);
1226
+
1227
+ // Update objects matched
1228
+ $this->scans->set_scan_summary_objects_match($this->scan->id, 'comments');
1229
+
1230
+ // Totals of content for each URL status added
1231
+ $added = array_unique(array_merge($links_added, $images_added, $authors_added));
1232
+ foreach ($added as $url_id)
1233
+ $this->scans->set_scan_url_status_total_content($url_id, $this->scan->id);
1234
+
1235
+ // Check inspect data
1236
+ if ($this->inspect())
1237
+ return false;
1238
+ }
1239
+
1240
+ // Empty
1241
+ } elseif ($this->scans->get_scan_objects_count($this->scan->id, 'comments') >= $this->total_comments) {
1242
+
1243
+ // Update populated status
1244
+ $this->scans->update_scan_trace($this->scan->id, array('populated_comments' => true));
1245
+
1246
+ // Check inspect data
1247
+ if ($this->inspect())
1248
+ return false;
1249
+
1250
+ // Check data to inspect
1251
+ } elseif ($this->inspect()) {
1252
+
1253
+ // Working
1254
+ return false;
1255
+ }
1256
+
1257
+ // End or next comment
1258
+ return $this->scans->get_scan_trace($this->scan->id, 'populated_comments')? true : $this->content_comments();
1259
+ }
1260
+
1261
+
1262
+
1263
+ /**
1264
+ * Retrieve next comment
1265
+ */
1266
+ private function content_comments_next($exceptions = array()) {
1267
+
1268
+ // Globals
1269
+ global $wpdb;
1270
+
1271
+ // Initialize
1272
+ $where = '1 = 1';
1273
+
1274
+ // Check exceptions
1275
+ if (!empty($exceptions))
1276
+ $where .= ' AND comment_ID NOT IN ('.implode(',', array_map('intval', $exceptions)).')';
1277
+
1278
+ // Check last date condition
1279
+ $last_date_gmt = $this->scans->get_scan_trace($this->scan->id, 'last_comment_date_gmt');
1280
+ if (!empty($last_date_gmt))
1281
+ $where .= ' AND comment_date_gmt '.(('asc' == $this->scan->crawl_order)? '>=' : '<=').' "'.esc_sql($last_date_gmt).'"';
1282
+
1283
+ // Set arguments
1284
+ $this->set_comment_args();
1285
+
1286
+ // Retrieve next comment
1287
+ $comment = $wpdb->get_row('SELECT comment_ID, comment_post_ID, comment_content, comment_author, comment_author_url, comment_date_gmt FROM '.$wpdb->comments.' WHERE '.$where.' '.$this->comment_args['comment_status'].$this->comment_args['time_scope'].$this->comment_args['content_filters'].' ORDER BY '.$this->comment_args['order_by'].' LIMIT 1');
1288
+
1289
+ // Check if comment id previously registered
1290
+ if (!empty($comment) && $this->scans->scan_object_exists($this->scan->id, $comment->comment_ID, 'comments')) {
1291
+
1292
+ // Prepare identifiers
1293
+ $ids = array($comment->comment_ID);
1294
+
1295
+ // Check same value of last date
1296
+ if ($comment->comment_date_gmt == $last_date_gmt) {
1297
+
1298
+ // Check more identifiers with same date
1299
+ $existing_ids = $this->scans->get_scan_objects_ids_by_date($this->scan->id, 'comments', $last_date_gmt);
1300
+ if (!empty($existing_ids) && is_array($existing_ids))
1301
+ $ids = array_unique(array_merge($ids, $existing_ids));
1302
+ }
1303
+
1304
+ // Add exceptions
1305
+ $exceptions = empty($exceptions)? $ids : array_unique(array_merge($exceptions, $ids));
1306
+
1307
+ // Next comment with exceptions
1308
+ $comment = $this->content_comments_next($exceptions);
1309
+ }
1310
+
1311
+ // Done
1312
+ return empty($comment)? false : $comment;
1313
+ }
1314
+
1315
+
1316
+
1317
+ /**
1318
+ * Obtains and process links from the blogroll
1319
+ */
1320
+ private function content_blogroll() {
1321
+
1322
+ // Recursion limit
1323
+ static $recursion;
1324
+ $recursion = isset($recursion)? 1 : $recursion + 1;
1325
+ if ($recursion >= wplnst_get_nsetting('recursion_limit')) {
1326
+ $this->restart();
1327
+ return false;
1328
+ }
1329
+
1330
+ // Content max pack
1331
+ $this->pack_index++;
1332
+ if ($this->pack_index > wplnst_get_nsetting('max_pack')) {
1333
+ $this->restart();
1334
+ return false;
1335
+ }
1336
+
1337
+ // Check next blogroll link
1338
+ if (false !== ($blogroll_link = $this->content_blogroll_next())) {
1339
+
1340
+ // Register object
1341
+ if (!$this->scans->register_scan_object($this->scan->id, $blogroll_link->link_id, 'blogroll'))
1342
+ return $this->content_blogroll();
1343
+
1344
+ // Update last blogroll id
1345
+ $this->scans->update_scan_trace($this->scan->id, array('last_blogroll_id' => $blogroll_link->link_id));
1346
+
1347
+ // Update blogroll index
1348
+ $this->scans->update_scan_trace($this->scan->id, array('blogroll_index' => $this->scans->get_scan_objects_count($this->scan->id, 'blogroll')));
1349
+
1350
+ // Process link URL
1351
+ if (!empty($blogroll_link->link_url)) {
1352
+
1353
+ // Check URL
1354
+ $urlinfo = $this->urlo->parse($blogroll_link->link_url);
1355
+ if ($this->filter_include_urls($urlinfo['url']) && $this->filter_exclude_urls($urlinfo['url'])) {
1356
+
1357
+ // Check anchor
1358
+ if ($this->filter_anchor_text($blogroll_link->link_name)) {
1359
+
1360
+ // Create link array
1361
+ $link = array_merge($urlinfo, array(
1362
+ 'anchor' => $blogroll_link->link_name,
1363
+ 'link_type' => 'links',
1364
+ 'object_id' => $blogroll_link->link_id,
1365
+ 'object_type' => 'blogroll',
1366
+ 'object_post_type' => "",
1367
+ 'object_field' => 'link_url',
1368
+ 'object_date_gmt' => '0000-00-00 00:00:00',
1369
+ ));
1370
+
1371
+ // Add blogroll link
1372
+ $blogroll_added = $this->save(array($link));
1373
+
1374
+ // Update URLs phases
1375
+ $this->scans->set_scan_summary_urls_phases($this->scan->id);
1376
+
1377
+ // Update objects matched
1378
+ $this->scans->set_scan_summary_objects_match($this->scan->id, 'blogroll');
1379
+
1380
+ // Totals of content for each URL status added
1381
+ foreach ($blogroll_added as $url_id)
1382
+ $this->scans->set_scan_url_status_total_content($url_id, $this->scan->id);
1383
+
1384
+ // Check inspect data
1385
+ if ($this->inspect())
1386
+ return false;
1387
+ }
1388
+ }
1389
+ }
1390
+
1391
+ // Empty
1392
+ } elseif ($this->scans->get_scan_objects_count($this->scan->id, 'blogroll') >= $this->total_blogroll) {
1393
+
1394
+ // Update populated status
1395
+ $this->scans->update_scan_trace($this->scan->id, array('populated_blogroll' => true));
1396
+
1397
+ // Check inspect data
1398
+ if ($this->inspect())
1399
+ return false;
1400
+
1401
+ // Check data to inspect
1402
+ } elseif ($this->inspect()) {
1403
+
1404
+ // Working
1405
+ return false;
1406
+ }
1407
+
1408
+ // End or next blogroll link
1409
+ return $this->scans->get_scan_trace($this->scan->id, 'populated_blogroll')? true : $this->content_blogroll();
1410
+ }
1411
+
1412
+
1413
+
1414
+ /**
1415
+ * Retrieve next blogroll link
1416
+ */
1417
+ private function content_blogroll_next($exceptions = array()) {
1418
+
1419
+ // Globals
1420
+ global $wpdb;
1421
+
1422
+ // Initialize
1423
+ $where = '1 = 1';
1424
+
1425
+ // Check exceptions
1426
+ if (!empty($exceptions))
1427
+ $where .= ' AND link_id NOT IN ('.implode(',', array_map('intval', $exceptions)).')';
1428
+
1429
+ // Check last identifier condition
1430
+ $last_id = (int) $this->scans->get_scan_trace($this->scan->id, 'last_blogroll_id');
1431
+ if (!empty($last_id))
1432
+ $where .= ' AND link_id '.(('asc' == $this->scan->crawl_order)? '>' : '<').' '.$last_id;
1433
+
1434
+ // Set arguments
1435
+ $this->set_blogroll_args();
1436
+
1437
+ // Query next blogroll link
1438
+ $bookmark = $wpdb->get_row('SELECT link_id, link_url, link_name FROM '.$wpdb->links.' WHERE '.$where.' ORDER BY '.$this->blogroll_args['order_by'].' LIMIT 1');
1439
+
1440
+ // Check if bookmark is previously registered
1441
+ if (!empty($bookmark) && $this->scans->scan_object_exists($this->scan->id, $bookmark->link_id, 'blogroll')) {
1442
+ $exceptions[] = $bookmark->link_id;
1443
+ $bookmark = $this->content_blogroll_next($exceptions);
1444
+ }
1445
+
1446
+ // Done
1447
+ return empty($bookmark)? false : $bookmark;
1448
+ }
1449
+
1450
+
1451
+
1452
+ /**
1453
+ * Extract links from content
1454
+ */
1455
+ private function extract_links($content, $parent_url, $object_id, $object_type, $object_post_type, $object_field, $object_date_gmt) {
1456
+
1457
+ // Initialize
1458
+ $links = array();
1459
+
1460
+ // Check links
1461
+ if (preg_match_all('/(<a[^>]+href=["|\'](.+)["|\'][^>]*>)(.*)<\/a>/isUu', $content, $matches, PREG_SET_ORDER) > 0) {
1462
+
1463
+ // Enum matched links tags
1464
+ foreach ($matches as $match) {
1465
+
1466
+ // Skip page anchor
1467
+ if ('#' == mb_substr($match[2], 0, 1))
1468
+ continue;
1469
+
1470
+ // Check anchor
1471
+ $anchor = $match[3];
1472
+ if (!$this->filter_anchor_text($anchor))
1473
+ continue;
1474
+
1475
+ // Check URL
1476
+ $urlinfo = $this->urlo->parse($match[2], $parent_url);
1477
+ if (!$this->filter_include_urls($urlinfo['url']) || !$this->filter_exclude_urls($urlinfo['url']))
1478
+ continue;
1479
+
1480
+ // Check attributes
1481
+ $attributes = $this->urlo->extract_attributes($match[1]);
1482
+ if (!$this->filter_html_attributes('a', $attributes))
1483
+ continue;
1484
+
1485
+ // Check nofollow
1486
+ $nofollow = false;
1487
+ if (!empty($attributes['rel'])) {
1488
+ $values = explode(' ', strtolower(str_replace("\n", ' ', str_replace("\r", ' ', $attributes['rel']))));
1489
+ if (in_array('nofollow', $values))
1490
+ $nofollow = true;
1491
+ }
1492
+
1493
+ // Add item
1494
+ $links[] = array_merge($urlinfo, array(
1495
+ 'chunk' => $match[0],
1496
+ 'anchor' => $anchor,
1497
+ 'nofollow' => $nofollow,
1498
+ 'attributes' => $attributes,
1499
+ 'link_type' => 'links',
1500
+ 'object_id' => $object_id,
1501
+ 'object_type' => $object_type,
1502
+ 'object_post_type' => $object_post_type,
1503
+ 'object_field' => $object_field,
1504
+ 'object_date_gmt' => $object_date_gmt,
1505
+ ));
1506
+ }
1507
+ }
1508
+
1509
+ // Done
1510
+ return $links;
1511
+ }
1512
+
1513
+
1514
+
1515
+ /**
1516
+ * Extract images from content
1517
+ */
1518
+ private function extract_images($content, $parent_url, $object_id, $object_type, $object_post_type, $object_field, $object_date_gmt) {
1519
+
1520
+ // Initialize
1521
+ $images = array();
1522
+
1523
+ // Check links
1524
+ if (preg_match_all('/<img[^>]+src=["|\'](.+)["|\'][^>]*>/isUu', $content, $matches, PREG_SET_ORDER) > 0) {
1525
+
1526
+ // Enum matched links tags
1527
+ foreach ($matches as $match) {
1528
+
1529
+ // Skip page anchor
1530
+ if ('#' == mb_substr($match[1], 0, 1))
1531
+ continue;
1532
+
1533
+ // Check URL
1534
+ $urlinfo = $this->urlo->parse($match[1], $parent_url);
1535
+ if (!$this->filter_include_urls($urlinfo['url']) || !$this->filter_exclude_urls($urlinfo['url']))
1536
+ continue;
1537
+
1538
+ // Check attributes
1539
+ $attributes = $this->urlo->extract_attributes($match[0]);
1540
+ if (!$this->filter_html_attributes('img', $attributes))
1541
+ continue;
1542
+
1543
+ // Add item
1544
+ $images[] = array_merge($urlinfo, array(
1545
+ 'chunk' => $match[0],
1546
+ 'attributes' => $attributes,
1547
+ 'link_type' => 'images',
1548
+ 'object_id' => $object_id,
1549
+ 'object_type' => $object_type,
1550
+ 'object_post_type' => $object_post_type,
1551
+ 'object_field' => $object_field,
1552
+ 'object_date_gmt' => $object_date_gmt,
1553
+ ));
1554
+ }
1555
+ }
1556
+
1557
+ // Done
1558
+ return $images;
1559
+ }
1560
+
1561
+
1562
+
1563
+ // Filter content
1564
+ // ---------------------------------------------------------------------------------------------------
1565
+
1566
+
1567
+
1568
+ /**
1569
+ * Check if passes anchor text filters
1570
+ */
1571
+ private function filter_anchor_text($anchor) {
1572
+
1573
+ // Check filters
1574
+ if (empty($this->scan->anchor_filters))
1575
+ return true;
1576
+
1577
+ // Initialize
1578
+ $passed = array();
1579
+
1580
+ // Version without tags
1581
+ $anchor_text = strip_tags($anchor);
1582
+
1583
+ // Enum filters
1584
+ foreach ($this->scan->anchor_filters as $filter) {
1585
+
1586
+ // Check filter
1587
+ if (empty($filter['type']) || !isset($filter['value']))
1588
+ continue;
1589
+
1590
+ // Check if contains a value
1591
+ if ('contains' == $filter['type']) {
1592
+
1593
+ // Continue if passed
1594
+ if (isset($passed['contains']) && true === $passed['contains'])
1595
+ continue;
1596
+
1597
+ // Check tag in filter
1598
+ $is_filter_tag = (false !== strpos($filter['value'], '<') || false !== strpos($filter['value'], '>'));
1599
+
1600
+ // Test
1601
+ $passed['contains'] = (false !== stripos($anchor_text, $filter['value'])) || ($is_filter_tag && false !== stripos($anchor, $filter['value']));
1602
+
1603
+ // Check if not contain a value
1604
+ } elseif ('not-contains' == $filter['type']) {
1605
+
1606
+ // Continue if passed
1607
+ if (isset($passed['contains']) && false === $passed['contains'])
1608
+ continue;
1609
+
1610
+ // Check tag in filter
1611
+ $is_filter_tag = (false !== strpos($filter['value'], '<') || false !== strpos($filter['value'], '>'));
1612
+
1613
+ // Test
1614
+ $passed['not-contains'] = (false === stripos($anchor_text, $filter['value'])) || ($is_filter_tag && false === stripos($anchor, $filter['value']));
1615
+
1616
+ // Check if equal to a value
1617
+ } elseif ('equal-to' == $filter['type']) {
1618
+
1619
+ // Continue if passed
1620
+ if (isset($passed['equal-to']) && true === $passed['equal-to'])
1621
+ continue;
1622
+
1623
+ // Test
1624
+ $passed['equal-to'] = ($anchor == $filter['value']);
1625
+
1626
+ // Check if not equal to a value
1627
+ } elseif ('not-equal-to' == $filter['type']) {
1628
+
1629
+ // Continue if passed
1630
+ if (isset($passed['not-equal-to']) && false === $passed['not-equal-to'])
1631
+ continue;
1632
+
1633
+ // Test
1634
+ $passed['not-equal-to'] = ($anchor != $filter['value']);
1635
+
1636
+ // Check if begins with value
1637
+ } elseif ('begins-with' == $filter['type']) {
1638
+
1639
+ // Continue if passed
1640
+ if (isset($passed['begins-with']) && true === $passed['begins-with'])
1641
+ continue;
1642
+
1643
+ // Test
1644
+ $passed['begins-with'] = (0 === stripos($anchor, $filter['value']));
1645
+
1646
+ // Check if ends with value
1647
+ } elseif ('ends-by' == $filter['type']) {
1648
+
1649
+ // Continue if passed
1650
+ if (isset($passed['ends-by']) && true === $passed['ends-by'])
1651
+ continue;
1652
+
1653
+ // Test
1654
+ $passed['ends-by'] = (($temp = strlen($anchor) - strlen($filter['value'])) >= 0 && stripos($anchor, $filter['value'], $temp) !== false);
1655
+
1656
+ // Check if is empty
1657
+ } elseif ('empty' == $filter['type']) {
1658
+
1659
+ // Continue if passed
1660
+ if (isset($passed['empty']))
1661
+ continue;
1662
+
1663
+ // Test
1664
+ $passed['empty'] = ($anchor === '');
1665
+ }
1666
+ }
1667
+
1668
+ // Check filters
1669
+ $checks = array('contains', 'not-contains', 'equal-to', 'not-equal', 'begins-with', 'ends-by', 'empty');
1670
+ foreach ($checks as $check) {
1671
+ if (isset($passed[$check]) && false === $passed[$check])
1672
+ return false;
1673
+ }
1674
+
1675
+ // Done
1676
+ return true;
1677
+ }
1678
+
1679
+
1680
+
1681
+ /**
1682
+ * Filter for URL including
1683
+ */
1684
+ private function filter_include_urls($url) {
1685
+
1686
+ // Check filters
1687
+ if (empty($this->scan->include_urls))
1688
+ return true;
1689
+
1690
+ // Initialize
1691
+ $passed = array();
1692
+
1693
+ // Enum filters
1694
+ foreach ($this->scan->include_urls as $filter) {
1695
+
1696
+ // Check filter
1697
+ if (empty($filter['type']) || !isset($filter['value']))
1698
+ continue;
1699
+
1700
+ // Check if match all value
1701
+ if ('full-url' == $filter['type']) {
1702
+
1703
+ // Continue if passed
1704
+ if (isset($passed['full-url']) && true === $passed['full-url'])
1705
+ continue;
1706
+
1707
+ // Test
1708
+ $passed['full-url'] = ($url == $filter['value']);
1709
+
1710
+ // Check if match a value
1711
+ } elseif ('matched-string' == $filter['type']) {
1712
+
1713
+ // Continue if passed
1714
+ if (isset($passed['matched-string']) && true === $passed['matched-string'])
1715
+ continue;
1716
+
1717
+ // Test
1718
+ $passed['matched-string'] = (false !== stripos($url, $filter['value']));
1719
+
1720
+ // Check if is prefixed
1721
+ } elseif ('url-prefix' == $filter['type']) {
1722
+
1723
+ // Continue if passed
1724
+ if (isset($passed['url-prefix']) && true === $passed['url-prefix'])
1725
+ continue;
1726
+
1727
+ // Test
1728
+ $passed['url-prefix'] = (0 === stripos($url, $filter['value']));
1729
+
1730
+ // Check if ends with value
1731
+ } elseif ('url-suffix' == $filter['type']) {
1732
+
1733
+ // Continue if passed
1734
+ if (isset($passed['url-suffix']) && true === $passed['url-suffix'])
1735
+ continue;
1736
+
1737
+ // Test
1738
+ $passed['url-suffix'] = (($temp = strlen($url) - strlen($filter['value'])) >= 0 && stripos($url, $filter['value'], $temp) !== false);
1739
+ }
1740
+ }
1741
+
1742
+ // Check filters
1743
+ $checks = array('full-url', 'matched-string', 'url-prefix', 'url-suffix');
1744
+ foreach ($checks as $check) {
1745
+ if (isset($passed[$check]) && false === $passed[$check])
1746
+ return false;
1747
+ }
1748
+
1749
+ // Done
1750
+ return true;
1751
+ }
1752
+
1753
+
1754
+
1755
+ /**
1756
+ * Filter for URL excluding
1757
+ */
1758
+ private function filter_exclude_urls($url) {
1759
+
1760
+ // Check filters
1761
+ if (empty($this->scan->exclude_urls))
1762
+ return true;
1763
+
1764
+ // Initialize
1765
+ $passed = array();
1766
+
1767
+ // Enum filters
1768
+ foreach ($this->scan->exclude_urls as $filter) {
1769
+
1770
+ // Check filter
1771
+ if (empty($filter['type']) || !isset($filter['value']))
1772
+ continue;
1773
+
1774
+ // Check if match all value
1775
+ if ('full-url' == $filter['type']) {
1776
+
1777
+ // Continue if passed
1778
+ if (isset($passed['full-url']) && false === $passed['full-url'])
1779
+ continue;
1780
+
1781
+ // Test
1782
+ $passed['full-url'] = ($url != $filter['value']);
1783
+
1784
+ // Check if match a value
1785
+ } elseif ('matched-string' == $filter['type']) {
1786
+
1787
+ // Continue if passed
1788
+ if (isset($passed['matched-string']) && false === $passed['matched-string'])
1789
+ continue;
1790
+
1791
+ // Test
1792
+ $passed['matched-string'] = (false === stripos($url, $filter['value']));
1793
+
1794
+ // Check if is prefixed
1795
+ } elseif ('url-prefix' == $filter['type']) {
1796
+
1797
+ // Continue if passed
1798
+ if (isset($passed['url-prefix']) && false === $passed['url-prefix'])
1799
+ continue;
1800
+
1801
+ // Test
1802
+ $passed['url-prefix'] = (0 !== stripos($url, $filter['value']));
1803
+
1804
+ // Check if ends with value
1805
+ } elseif ('url-suffix' == $filter['type']) {
1806
+
1807
+ // Continue if passed
1808
+ if (isset($passed['url-suffix']) && false === $passed['url-suffix'])
1809
+ continue;
1810
+
1811
+ // Test
1812
+ $passed['url-suffix'] = (($temp = strlen($url) - strlen($filter['value'])) >= 0 && stripos($url, $filter['value'], $temp) === false);
1813
+ }
1814
+ }
1815
+
1816
+ // Check filters
1817
+ $checks = array('full-url', 'matched-string', 'url-prefix', 'url-suffix');
1818
+ foreach ($checks as $check) {
1819
+ if (isset($passed[$check]) && false === $passed[$check])
1820
+ return false;
1821
+ }
1822
+
1823
+ // Done
1824
+ return true;
1825
+ }
1826
+
1827
+
1828
+
1829
+ /**
1830
+ * Filter based on attributes
1831
+ */
1832
+ private function filter_html_attributes($element, $attributes) {
1833
+
1834
+ // Check filters
1835
+ if (empty($this->scan->html_attributes))
1836
+ return true;
1837
+
1838
+ // Initialize
1839
+ $passed = array();
1840
+
1841
+ // Isolate attributes
1842
+ $att_names = array_keys($attributes);
1843
+
1844
+ // Enum filters
1845
+ foreach ($this->scan->html_attributes as $filter) {
1846
+
1847
+ // Check filter
1848
+ if (empty($filter['element']) || $element != $filter['element'] || !isset($filter['att']) || empty($filter['having']))
1849
+ continue;
1850
+
1851
+ // Check if not have an attribute
1852
+ if ('not-have' == $filter['having']) {
1853
+
1854
+ // Continue if not passed
1855
+ if (isset($passed['not-have']) && false === $passed['not-have'])
1856
+ continue;
1857
+
1858
+ // Test
1859
+ $passed['not-have'] = !in_array($filter['att'], $att_names);
1860
+
1861
+ // Contains
1862
+ } elseif ('contains' == $filter['op']) {
1863
+
1864
+ // Continue if passed
1865
+ if (isset($passed['contains']) && true === $passed['contains'])
1866
+ continue;
1867
+
1868
+ // Need a filter value
1869
+ if (!isset($filter['value']) || '' === ''.trim($filter['value'])) {
1870
+ $passed['contains'] = false;
1871
+ continue;
1872
+ }
1873
+
1874
+ // Check att name compatibility
1875
+ if (!in_array($filter['att'], $att_names)) {
1876
+ $passed['contains'] = false;
1877
+ continue;
1878
+ }
1879
+
1880
+ // Final test
1881
+ $passed['contains'] = in_array(strtolower($filter['value']), $att_values)? $filter['value'] : false;
1882
+
1883
+ // Not contains
1884
+ } elseif ('not-contains' == $filter['op']) {
1885
+
1886
+ // Continue if passed
1887
+ if (isset($passed['not-contains']) && true === $passed['not-contains'])
1888
+ continue;
1889
+
1890
+ // Need a filter value
1891
+ if (!isset($filter['value']) || '' === ''.trim($filter['value'])) {
1892
+ $passed['not-contains'] = false;
1893
+ continue;
1894
+ }
1895
+
1896
+ // Check att name compatibility
1897
+ if (!in_array($filter['att'], $att_names)) {
1898
+ $passed['not-contains'] = false;
1899
+ continue;
1900
+ }
1901
+
1902
+ // Final test
1903
+ $passed['not-contains'] = !in_array(strtolower($filter['value']), $att_values)? $filter['value'] : false;
1904
+
1905
+ // Equal
1906
+ } elseif ('equal' == $filter['op']) {
1907
+
1908
+ // Continue if passed
1909
+ if (isset($passed['equal']) && true === $passed['equal'])
1910
+ continue;
1911
+
1912
+ // Test
1913
+ $passed['equal'] = in_array($filter['att'], $att_names)? ($attributes[$filter['att']] == (isset($filter['value'])? $filter['value'] : '')) : false;
1914
+
1915
+ // Not equal
1916
+ } elseif ('not-equal' == $filter['op']) {
1917
+
1918
+ // Continue if passed
1919
+ if (isset($passed['not-equal']) && false === $passed['not-equal'])
1920
+ continue;
1921
+
1922
+ // Test
1923
+ $passed['not-equal'] = in_array($filter['att'], $att_names)? ($attributes[$filter['att']] != (isset($filter['value'])? $filter['value'] : '')) : false;
1924
+
1925
+ // Not empty value
1926
+ } elseif ('not-empty' == $filter['op']) {
1927
+
1928
+ // Continue if passed
1929
+ if (isset($passed['not-empty']) && false === $passed['not-empty'])
1930
+ continue;
1931
+
1932
+ // Test
1933
+ $passed['not-empty'] = in_array($filter['att'], $att_names)? ($attributes[$filter['att']] !== '') : false;
1934
+
1935
+ // Empty value
1936
+ } elseif ('empty' == $filter['op']) {
1937
+
1938
+ // Continue if passed
1939
+ if (isset($passed['empty']) && false === $passed['empty'])
1940
+ continue;
1941
+
1942
+ // Test
1943
+ $passed['empty'] = in_array($filter['att'], $att_names)? ($attributes[$filter['att']] === '') : false;
1944
+ }
1945
+ }
1946
+
1947
+ // Check filters
1948
+ $checks = array('not-have', 'contains', 'not-contains', 'equal', 'not-equal', 'not-empty', 'empty');
1949
+ foreach ($checks as $check) {
1950
+ if (isset($passed[$check]) && false === $passed[$check])
1951
+ return false;
1952
+ }
1953
+
1954
+ // Done
1955
+ return true;
1956
+ }
1957
+
1958
+
1959
+
1960
+ /**
1961
+ * Extract filter content for queries
1962
+ */
1963
+ private function filter_for_queries() {
1964
+
1965
+
1966
+ /* Initialization */
1967
+
1968
+ // Check cache
1969
+ static $matches;
1970
+ if (isset($matches))
1971
+ return $matches;
1972
+ $matches = array();
1973
+
1974
+ // Check configuration
1975
+ if (!$this->scan->filtered_query)
1976
+ return $matches;
1977
+
1978
+
1979
+ /* Anchor filters */
1980
+
1981
+ // Check anchor filters
1982
+ if (!empty($this->scan->anchor_filters)) {
1983
+
1984
+ // Enum anchor filters
1985
+ foreach ($this->scan->anchor_filters as $filter) {
1986
+
1987
+ // Check filter
1988
+ if (empty($filter['type']) || !isset($filter['value']) || '' === ''.trim($filter['value']))
1989
+ continue;
1990
+
1991
+ // Match filters
1992
+ if (in_array($filter['type'], array('contains', 'equal-to', 'begins-with', 'ends-by'))) {
1993
+
1994
+ // Need this string
1995
+ $matches[] = $filter['value'];
1996
+ }
1997
+ }
1998
+ }
1999
+
2000
+
2001
+ /* Include URLs */
2002
+
2003
+ // Check include filters
2004
+ if (!empty($this->scan->include_urls)) {
2005
+
2006
+ // Enum include filters
2007
+ foreach ($this->scan->include_urls as $filter) {
2008
+
2009
+ // Check filter
2010
+ if (empty($filter['type']) || !isset($filter['value']) || '' === ''.trim($filter['value']))
2011
+ continue;
2012
+
2013
+ // Match filters
2014
+ if (in_array($filter['type'], array('full-url', 'matched-string', 'url-prefix', 'url-suffix'))) {
2015
+
2016
+ // Need this string
2017
+ $matches[] = $filter['value'];
2018
+ }
2019
+ }
2020
+ }
2021
+
2022
+
2023
+ /* HTML attributes */
2024
+
2025
+ // Check attributes filters
2026
+ if (!empty($this->scan->html_attributes)) {
2027
+
2028
+ // Enum filters
2029
+ foreach ($this->scan->html_attributes as $filter) {
2030
+
2031
+ // Check filter value
2032
+ if (!isset($filter['value']) || '' === ''.trim($filter['value']))
2033
+ continue;
2034
+
2035
+ // Check having and attribute part
2036
+ if (empty($filter['having']) || 'have' != $filter['having'] || empty($filter['att']) || '' == 'att'.trim($filter['att']))
2037
+ continue;
2038
+
2039
+ // Check operation
2040
+ if (empty($filter['op']) || !in_array($filter['op'], array('contains', 'equal')))
2041
+ continue;
2042
+
2043
+ // Add string
2044
+ $matches[] = $filter['value'];
2045
+ }
2046
+ }
2047
+
2048
+
2049
+ // Done
2050
+ return $matches;
2051
+ }
2052
+
2053
+
2054
+
2055
+ // Data access to store content data
2056
+ // ---------------------------------------------------------------------------------------------------
2057
+
2058
+
2059
+
2060
+ /**
2061
+ * Save links
2062
+ */
2063
+ private function save($links, $parent_post_id = 0) {
2064
+
2065
+ // Initialize
2066
+ $statuses = array();
2067
+
2068
+ // Enum links
2069
+ foreach ($links as $link) {
2070
+
2071
+ // Avoid empty URLs
2072
+ if ('' === ''.trim($link['url']))
2073
+ continue;
2074
+
2075
+ // Malformed links tracking
2076
+ if ($link['malformed']) {
2077
+ if (!$this->scan->malformed)
2078
+ continue;
2079
+ }
2080
+
2081
+ // Search by main URL
2082
+ if (false !== ($row = $this->scans->get_scan_url(array('url' => $link['url'], 'no_cache' => true)))) {
2083
+ $url_id = $row->url_id;
2084
+
2085
+ // Register scan URL
2086
+ } elseif (false === ($url_id = $this->scans->add_scan_url($link, $this->scan->id))) {
2087
+ continue;
2088
+ }
2089
+
2090
+ // Check destination type
2091
+ if (isset($link['scope']) && in_array($this->scan->destination_type, array('internal', 'external')) && $link['scope'] != $this->scan->destination_type)
2092
+ continue;
2093
+
2094
+ // Save URL location
2095
+ $this->scans->add_scan_url_location($url_id, $this->scan->id, $link);
2096
+
2097
+ // Check our tiny cache
2098
+ if (in_array($url_id, $statuses))
2099
+ continue;
2100
+
2101
+ // Check if exists a previous status identifier
2102
+ if (false !== $this->scans->get_scan_url_status(array('url_id' => $url_id, 'scan_id' => $this->scan->id))) {
2103
+ $statuses[] = $url_id;
2104
+ continue;
2105
+ }
2106
+
2107
+ // Check phase
2108
+ $phase = $this->urlo->is_crawleable($link)? 'wait' : 'end';
2109
+
2110
+ // Create URL status
2111
+ $this->scans->add_scan_url_status($url_id, $this->scan->id, $phase);
2112
+
2113
+ // Status on
2114
+ $statuses[] = $url_id;
2115
+ }
2116
+
2117
+ // Done
2118
+ return $statuses;
2119
+ }
2120
+
2121
+
2122
+
2123
+ // Utilities functions
2124
+ // ---------------------------------------------------------------------------------------------------
2125
+
2126
+
2127
+
2128
+ /**
2129
+ * Cast relative URLs to absolute
2130
+ */
2131
+ public function parent_permalink($parent) {
2132
+
2133
+ // Check array
2134
+ if (!is_array($parent) || !isset($parent['post_id']))
2135
+ return false;
2136
+
2137
+ // Check identifier
2138
+ $post_id = (int) $parent['post_id'];
2139
+ if (empty($post_id))
2140
+ return false;
2141
+
2142
+ // Return permalink
2143
+ return $this->get_permalink($post_id);
2144
+ }
2145
+
2146
+
2147
+
2148
+ /**
2149
+ * Retrieve permalink based on WP redirection.
2150
+ * Here is too early to call WP get_permalink function,
2151
+ * so we use the short permalink form to catch the redirection.
2152
+ */
2153
+ private function get_permalink($post_id) {
2154
+
2155
+ // Permalinks cache
2156
+ static $permalinks;
2157
+ if (!isset($permalinks))
2158
+ $permalinks = array();
2159
+
2160
+ // Check cache
2161
+ if (isset($permalinks[$post_id]))
2162
+ return $permalinks[$post_id];
2163
+
2164
+ // Initialize
2165
+ $permalinks[$post_id] = false;
2166
+
2167
+ // Load cURL wrapper library
2168
+ wplnst_require('core', 'curl');
2169
+
2170
+ // cURL request
2171
+ $response = WPLNST_Core_CURL::request(array(
2172
+ 'CURLOPT_URL' => $this->urlo->home_url.'/?p='.$post_id,
2173
+ 'CURLOPT_HEADER' => true,
2174
+ 'CURLOPT_NOBODY' => true,
2175
+ 'CURLOPT_HTTPHEADER' => array('Expect:'),
2176
+ 'CURLOPT_FOLLOWLOCATION' => false,
2177
+ 'CURLOPT_RETURNTRANSFER' => true,
2178
+ 'CURLOPT_FRESH_CONNECT' => true,
2179
+ 'CURLOPT_CONNECTTIMEOUT' => $this->connect_timeout,
2180
+ 'CURLOPT_TIMEOUT' => $this->request_timeout,
2181
+ 'CURLOPT_USERAGENT' => wplnst_get_tsetting('user_agent'),
2182
+ ));
2183
+
2184
+ // Check response
2185
+ if ($response['error'])
2186
+ return false;
2187
+
2188
+ // Check response
2189
+ $data = trim($response['data']);
2190
+ if (empty($data))
2191
+ return false;
2192
+
2193
+ // Enum response links
2194
+ $data = explode("\n", $data);
2195
+ foreach ($data as $line) {
2196
+
2197
+ // Clear line
2198
+ $line = trim(preg_replace('/\s+/', ' ', $line));
2199
+
2200
+ // Check redirection in status code
2201
+ if (!isset($status_code) && 0 === stripos($line, 'HTTP/')) {
2202
+ $line = explode(' ', $line);
2203
+ if (count($line) > 1) {
2204
+ $status_code = trim($line[1]);
2205
+ if ('30' != mb_substr($status_code, 0, 2))
2206
+ return false;
2207
+ }
2208
+
2209
+ // Check Location
2210
+ } elseif (isset($status_code) && 0 === stripos($line, 'location:')) {
2211
+ $location = trim(mb_substr($line, 9));
2212
+ $parts = @parse_url($location);
2213
+ if (!empty($parts['scheme']) && !empty($parts['host']))
2214
+ $permalinks[$post_id] = $location;
2215
+ }
2216
+ }
2217
+
2218
+ // Done
2219
+ return $permalinks[$post_id];
2220
+ }
2221
+
2222
+
2223
+
2224
+ // Internal debug
2225
+ private function debug($message) {
2226
+ wplnst_debug('scan '.$this->scan->id.' - thread '.$this->thread_id.' - '.$message, 'crawler');
2227
+ }
2228
+
2229
+
2230
+
2231
+ /**
2232
+ * Start again the crawler
2233
+ */
2234
+ protected function restart() {
2235
+ WPLNST_Core_Alive::run($this->scan->id, $this->scan->hash, $this->thread_id);
2236
+ }
2237
+
2238
+
2239
+
2240
+ /**
2241
+ * Calls alive activity method to check scans activity
2242
+ */
2243
+ protected function activity() {
2244
+ WPLNST_Core_Alive::activity(true);
2245
+ }
2246
+
2247
+
2248
+
2249
+ }
core/curl.php ADDED
@@ -0,0 +1,115 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * WP Link Status Core CURL class
5
+ *
6
+ * @package WP Link Status
7
+ * @subpackage WP Link Status Core
8
+ */
9
+ class WPLNST_Core_CURL {
10
+
11
+
12
+
13
+ /**
14
+ * Spawn async request
15
+ */
16
+ public static function spawn($setopts = array()) {
17
+ self::request(array_merge(array(
18
+ 'CURLOPT_HEADER' => false,
19
+ 'CURLOPT_NOBODY' => true,
20
+ 'CURLOPT_FOLLOWLOCATION' => false,
21
+ 'CURLOPT_RETURNTRANSFER' => false,
22
+ 'CURLOPT_FRESH_CONNECT' => true,
23
+ 'CURLOPT_CONNECTTIMEOUT' => 1,
24
+ 'CURLOPT_TIMEOUT' => 1,
25
+ ), $setopts));
26
+ }
27
+
28
+
29
+
30
+ /**
31
+ * Submit a POST request
32
+ */
33
+ public static function post($setopts = array(), $postfields = array()) {
34
+ return self::request(array_merge(array(
35
+ 'CURLOPT_HEADER' => false,
36
+ 'CURLOPT_NOBODY' => false,
37
+ 'CURLOPT_FOLLOWLOCATION' => false,
38
+ 'CURLOPT_RETURNTRANSFER' => true,
39
+ 'CURLOPT_FRESH_CONNECT' => true,
40
+ 'CURLOPT_POST' => true,
41
+ 'CURLOPT_POSTFIELDS' => http_build_query($postfields, null, '&'),
42
+ 'CURLOPT_HTTPHEADER' => array('Content-Type: application/x-www-form-urlencoded; charset=utf-8'),
43
+ ), $setopts));
44
+ }
45
+
46
+
47
+
48
+ /**
49
+ * Basic cURL wrapper
50
+ */
51
+ public static function request($setopts = array(), $getinfo = array()) {
52
+
53
+ // Default
54
+ $response = array(
55
+ 'data' => '',
56
+ 'info' => array(),
57
+ 'error' => true,
58
+ 'errno' => 0,
59
+ 'reason' => '',
60
+ 'time' => 0,
61
+ 'timestamp' => 0,
62
+ );
63
+
64
+ // Check cURL support
65
+ if (function_exists('wplnst_is_curl_enabled') && !wplnst_is_curl_enabled()) {
66
+ $response['reason'] = 'curl_disabled';
67
+ return $response;
68
+ }
69
+
70
+ // cURL session
71
+ if (false === ($ch = @curl_init())) {
72
+ $response['reason'] = 'curl_init';
73
+ return $response;
74
+ }
75
+
76
+ // Apply options
77
+ foreach ($setopts as $name => $value) {
78
+ if (defined($name))
79
+ @curl_setopt($ch, constant($name), $value);
80
+ }
81
+
82
+ // Now
83
+ $response['timestamp'] = time();
84
+
85
+ // Timer
86
+ $time_start = microtime(true);
87
+
88
+ // Retrieve response
89
+ $response['data'] = @curl_exec($ch);
90
+
91
+ // Time amount
92
+ $response['time'] = microtime(true) - $time_start;
93
+
94
+ // Copy error number
95
+ $response['errno'] = (int) @curl_errno($ch);
96
+
97
+ // Check info
98
+ foreach ($getinfo as $info) {
99
+ if (defined($info))
100
+ $response['info'][$info] = @curl_getinfo($ch, constant($info));
101
+ }
102
+
103
+ // Close connection
104
+ @curl_close($ch);
105
+
106
+ // Check error
107
+ $response['error'] = ($response['errno'] > 0);
108
+
109
+ // Done
110
+ return $response;
111
+ }
112
+
113
+
114
+
115
+ }
core/module.php ADDED
@@ -0,0 +1,195 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * WP Link Status Core Module class
5
+ *
6
+ * @package WP Link Status
7
+ * @subpackage WP Link Status Core
8
+ */
9
+ abstract class WPLNST_Core_Module {
10
+
11
+
12
+
13
+ // Properties
14
+ // ---------------------------------------------------------------------------------------------------
15
+
16
+
17
+
18
+ /**
19
+ * Scans object
20
+ */
21
+ public $scans;
22
+
23
+
24
+
25
+ /**
26
+ * URL object
27
+ */
28
+ public $url;
29
+
30
+
31
+
32
+ /**
33
+ * Singleton objects
34
+ */
35
+ private static $instances = array();
36
+
37
+
38
+
39
+ // Initialization
40
+ // ---------------------------------------------------------------------------------------------------
41
+
42
+
43
+
44
+ /**
45
+ * Private constructor, call plugin custom method
46
+ */
47
+ private function __construct($args = null) {
48
+ $this->on_construct($args);
49
+ }
50
+
51
+
52
+
53
+ /**
54
+ * Creates a singleton object instance
55
+ * Avoided use of get_called_class for PHP < 5.3 compatibility
56
+ */
57
+ protected static function get_instance($classname, $args = null) {
58
+ if (!isset(self::$instances[$classname]))
59
+ self::$instances[$classname] = new $classname($args);
60
+ return self::$instances[$classname];
61
+ }
62
+
63
+
64
+
65
+ /**
66
+ * Do nothing, to override
67
+ */
68
+ protected function on_construct($args = null) {}
69
+
70
+
71
+
72
+ // Plugins objects load
73
+ // ---------------------------------------------------------------------------------------------------
74
+
75
+
76
+
77
+ /**
78
+ * Return scans object
79
+ */
80
+ public function load_scans_object() {
81
+ if (!isset($this->scans)) {
82
+ wplnst_require('core', 'scans');
83
+ $this->scans = new WPLNST_Core_Scans();
84
+ }
85
+ }
86
+
87
+
88
+
89
+ /**
90
+ * Return URL object
91
+ */
92
+ public function load_url_object() {
93
+ if (!isset($this->urlo)) {
94
+ wplnst_require('core', 'url');
95
+ $this->urlo = new WPLNST_Core_URL();
96
+ }
97
+ }
98
+
99
+
100
+
101
+ // Wrapper methods
102
+ // ---------------------------------------------------------------------------------------------------
103
+
104
+
105
+
106
+ /**
107
+ * Wrapper method of WPLNST_Core_Scans class
108
+ */
109
+ public function get_scans($args) {
110
+ $this->load_scans_object();
111
+ return $this->scans->get_scans($args);
112
+ }
113
+
114
+
115
+
116
+ /**
117
+ * Wrapper method of WPLNST_Core_Scans class
118
+ */
119
+ public function get_scan_by_id($scan_id, $setup_names = false, $no_cache = false) {
120
+ $this->load_scans_object();
121
+ return $this->scans->get_scan_by_id($scan_id, $setup_names, $no_cache);
122
+ }
123
+
124
+
125
+
126
+ // AJAX wrappers
127
+ // ---------------------------------------------------------------------------------------------------
128
+
129
+
130
+
131
+ /**
132
+ * Check and initialize ajax respose
133
+ */
134
+ protected static function check_ajax_submit(&$response, $capability, $nonce = null) {
135
+
136
+ // Check default output
137
+ if (!isset($response))
138
+ $response = self::default_ajax_response($nonce);
139
+
140
+ // Check user capabilities
141
+ if (!current_user_can($capability)) {
142
+ $response['status'] = 'error';
143
+ $response['reason'] = __('Sorry, current user can`t perform this action', 'wplnst');
144
+ return false;
145
+
146
+ // Check if submitted nonce matches with the generated nonce we created earlier
147
+ } elseif (!isset($_POST['nonce']) || !wp_verify_nonce($_POST['nonce'], isset($nonce)? $nonce : __FILE__)) {
148
+ $response['status'] = 'error';
149
+ $response['reason'] = __('Sorry, security verification error. Please reload this page and try again.', 'wplnst');
150
+ return false;
151
+ }
152
+
153
+ // Submit Ok
154
+ return true;
155
+ }
156
+
157
+
158
+
159
+ /**
160
+ * Return array of ajax response
161
+ */
162
+ protected static function default_ajax_response($nonce = null) {
163
+ return array(
164
+ 'status' => 'ok',
165
+ 'reason' => '',
166
+ 'nonce' => (isset($nonce) && false === $nonce)? '' : wp_create_nonce(isset($nonce)? $nonce : __FILE__),
167
+ 'data' => array(),
168
+ );
169
+ }
170
+
171
+
172
+
173
+ /**
174
+ * Custom error ajax response
175
+ */
176
+ protected static function error_ajax_response($reason, $nonce = false) {
177
+ $response = self::default_ajax_response($nonce);
178
+ $response['status'] = 'error';
179
+ $response['reason'] = $reason;
180
+ self::output_ajax_response($response);
181
+ }
182
+
183
+
184
+
185
+ /**
186
+ * Output AJAX in JSON format and exit
187
+ */
188
+ protected static function output_ajax_response($response) {
189
+ @header('Content-Type: application/json');
190
+ die(@json_encode($response));
191
+ }
192
+
193
+
194
+
195
+ }
core/nonce/nonce.php ADDED
@@ -0,0 +1,310 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * WP Link Status Core Nonce class
5
+ *
6
+ * @package WP Link Status
7
+ * @subpackage WP Link Status Core
8
+ */
9
+ class WPLNST_Core_Nonce {
10
+
11
+
12
+
13
+ // Public methods
14
+ // ----------------------------------------------------------------------------------------
15
+
16
+
17
+
18
+ /**
19
+ * Create a new, user-independent, 24H valid nonce
20
+ */
21
+ public static function create_nonce($action = '') {
22
+ return substr(self::get_hash(self::nonce_tick().'|'.$action.'|'.__FILE__), -12, 10);
23
+ }
24
+
25
+
26
+
27
+ /*
28
+ * Check valid nonce
29
+ */
30
+ public static function verify_nonce($nonce, $action = '') {
31
+
32
+ // First check
33
+ $nonce = (string) $nonce;
34
+ if (empty($nonce))
35
+ return false;
36
+
37
+ // Time based "tick"
38
+ $tick = self::nonce_tick();
39
+
40
+ // Nonce generated 0-12 hours ago
41
+ $expected = substr(self::get_hash($tick.'|'.$action.'|'.__FILE__), -12, 10);
42
+ if (self::hash_equals($expected, $nonce))
43
+ return 1;
44
+
45
+ // Nonce generated 12-24 hours ago
46
+ $expected = substr(self::get_hash(($tick - 1).'|'.$action.'|'.__FILE__), -12, 10);
47
+ return self::hash_equals($expected, $nonce)? 2 : false;
48
+ }
49
+
50
+
51
+
52
+ /**
53
+ * Get system salt and hash data
54
+ */
55
+ public static function get_hash($data) {
56
+ $salt = self::get_salt();
57
+ return self::hash_hmac('md5', $data, $salt);
58
+ }
59
+
60
+
61
+
62
+ /**
63
+ * Generates random number
64
+ **/
65
+ public static function get_rand( $min = 0, $max = 0 ) {
66
+
67
+ global $wplnst_rnd_value;
68
+
69
+ // Reset $rnd_value after 14 uses
70
+ // 32(md5) + 40(sha1) + 40(sha1) / 8 = 14 random numbers from $rnd_value
71
+ if ( strlen($wplnst_rnd_value) < 8 ) {
72
+ $wplnst_rnd_value = md5(uniqid(microtime().mt_rand(), true));
73
+ $wplnst_rnd_value .= sha1($wplnst_rnd_value);
74
+ $wplnst_rnd_value .= sha1($wplnst_rnd_value);
75
+ }
76
+
77
+ // Take the first 8 digits for our value
78
+ $value = substr($wplnst_rnd_value, 0, 8);
79
+
80
+ // Strip the first eight, leaving the remainder for the next call to wp_rand().
81
+ $wplnst_rnd_value = substr($wplnst_rnd_value, 8);
82
+
83
+ $value = abs(hexdec($value));
84
+
85
+ // Some misconfigured 32bit environments (Entropy PHP, for example) truncate integers larger than PHP_INT_MAX to PHP_INT_MAX rather than overflowing them to floats.
86
+ $max_random_number = 3000000000 === 2147483647 ? (float) "4294967295" : 4294967295; // 4294967295 = 0xffffffff
87
+
88
+ // Reduce the value to be within the min - max range
89
+ if ($max != 0)
90
+ $value = $min + ($max - $min + 1) * $value / ($max_random_number + 1);
91
+
92
+ return abs(intval($value));
93
+ }
94
+
95
+
96
+
97
+ /**
98
+ * Password generation
99
+ */
100
+ public static function generate_password($length = 12, $special_chars = true, $extra_special_chars = false) {
101
+ $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
102
+ if ($special_chars)
103
+ $chars .= '!@#$%^&*()';
104
+ if ($extra_special_chars)
105
+ $chars .= '-_ []{}<>~`+=,.;:/?|';
106
+ $password = '';
107
+ for ($i = 0; $i < $length; $i++)
108
+ $password .= substr($chars, self::get_rand(0, strlen($chars) - 1), 1);
109
+ return $password;
110
+ }
111
+
112
+
113
+
114
+ /**
115
+ * Check and creates new salt file
116
+ */
117
+ public static function check_salt_file() {
118
+ return self::salt_file_ready()? true : self::create_salt_file();
119
+ }
120
+
121
+
122
+
123
+ /**
124
+ * Check salt file and constant
125
+ */
126
+ public static function salt_file_ready() {
127
+
128
+ // Check if file exists
129
+ $path = self::salt_file_path();
130
+ if (!@file_exists($path) || !@is_readable($path))
131
+ return false;
132
+
133
+ // Require
134
+ @require_once($path);
135
+ return defined('WPLNST_SALT');
136
+ }
137
+
138
+
139
+
140
+ /**
141
+ * Attempt to create a salt file
142
+ */
143
+ public static function create_salt_file() {
144
+ $path = self::salt_file_path();
145
+ if (!@is_writable(dirname($path)))
146
+ return false;
147
+ $result = @file_put_contents($path, "<?php /* Generated by WP Link Status: PLEASE DO NOT REMOVE OR MODIFY THIS FILE */"."\n"."define('WPLNST_SALT', '".self::generate_password(64, false, false)."');");
148
+ @chmod($path, 0664);
149
+ return (false !== $result && @file_exists($path) && @is_readable($path));
150
+ }
151
+
152
+
153
+
154
+ /**
155
+ * Remove custom salt file
156
+ */
157
+ public static function remove_salt_file() {
158
+ $path = self::salt_file_path();
159
+ if (@file_exists($path))
160
+ @unlink($path);
161
+ }
162
+
163
+
164
+
165
+ // Internal functions
166
+ // ----------------------------------------------------------------------------------------
167
+
168
+
169
+
170
+ /**
171
+ * Creates a "tick", time based
172
+ * This is a clone of WP nonce_tick function without constants and filters
173
+ */
174
+ private static function nonce_tick() {
175
+ return ceil(time()/(86400/2));
176
+ }
177
+
178
+
179
+
180
+ /**
181
+ * Unique salt to hash strings
182
+ */
183
+ private static function get_salt() {
184
+
185
+ // Process cache
186
+ static $cached_salt;
187
+ if (isset($cached_salt))
188
+ return $cached_salt;
189
+
190
+ // Salt from file
191
+ $salt = self::get_salt_stored();
192
+ if (empty($salt))
193
+ $salt = php_uname().'|'.phpversion().'|'.__FILE__;
194
+
195
+ // Store hash
196
+ $cached_salt = md5($salt);
197
+ return $cached_salt;
198
+ }
199
+
200
+
201
+
202
+ /**
203
+ * Return salt from config file
204
+ */
205
+ private static function get_salt_stored() {
206
+
207
+ // Require file
208
+ if (!defined('WPLNST_SALT'))
209
+ self::require_salt_file();
210
+
211
+ // Check constant
212
+ return defined('WPLNST_SALT')? WPLNST_SALT : false;
213
+ }
214
+
215
+
216
+
217
+ /**
218
+ * Check if file exists and require config file
219
+ */
220
+ private static function require_salt_file() {
221
+
222
+ // Check if file exists
223
+ $path = self::salt_file_path();
224
+ if (!@file_exists($path) || !@is_readable($path))
225
+ return false;
226
+
227
+ // Require
228
+ @require_once($path);
229
+ }
230
+
231
+
232
+
233
+ /**
234
+ * Salt path and filename
235
+ * Yest, it is very ugly
236
+ */
237
+ private static function salt_file_path() {
238
+ return dirname(dirname(dirname(dirname(dirname(__FILE__))))).'/wp-link-status-salt.php';
239
+ }
240
+
241
+
242
+
243
+ /**
244
+ * Wrapper for hash_hmac function
245
+ */
246
+ private static function hash_hmac($algo, $data, $key, $raw_output = false) {
247
+
248
+ // Check existing function
249
+ if (function_exists('hash_hmac')) {
250
+ return hash_hmac($algo, $data, $key, $raw_output);
251
+
252
+ // From WP
253
+ } else {
254
+
255
+ $packs = array('md5' => 'H32', 'sha1' => 'H40');
256
+
257
+ if (!isset($packs[$algo]))
258
+ return false;
259
+
260
+ $pack = $packs[$algo];
261
+
262
+ if (strlen($key) > 64)
263
+ $key = pack($pack, $algo($key));
264
+
265
+ $key = str_pad($key, 64, chr(0));
266
+
267
+ $ipad = (substr($key, 0, 64) ^ str_repeat(chr(0x36), 64));
268
+ $opad = (substr($key, 0, 64) ^ str_repeat(chr(0x5C), 64));
269
+
270
+ $hmac = $algo($opad.pack($pack, $algo($ipad.$data)));
271
+
272
+ if ($raw_output)
273
+ return pack($pack, $hmac);
274
+
275
+ return $hmac;
276
+ }
277
+ }
278
+
279
+
280
+
281
+ /**
282
+ * Wrapper for hash_equals function
283
+ */
284
+ private static function hash_equals($a, $b) {
285
+
286
+ // Check existing function
287
+ if (function_exists('hash_equals')) {
288
+ return hash_equals($a, $b);
289
+
290
+ // From WP
291
+ } else {
292
+
293
+ $a_length = strlen($a);
294
+ if ($a_length !== strlen($b))
295
+ return false;
296
+
297
+ $result = 0;
298
+
299
+ // Do not attempt to "optimize" this.
300
+ for ($i = 0; $i < $a_length; $i++) {
301
+ $result |= ord($a[$i]) ^ ord($b[$i]);
302
+ }
303
+
304
+ return $result === 0;
305
+ }
306
+ }
307
+
308
+
309
+
310
+ }
core/notify.php ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * WP Link Status Core Notify class
5
+ *
6
+ * @package WP Link Status
7
+ * @subpackage WP Link Status Core
8
+ */
9
+ class WPLNST_Core_Notify {
10
+
11
+
12
+
13
+ /**
14
+ * Send a notification of scan completed
15
+ */
16
+ public static function completed($scan) {
17
+
18
+ // Get current notifications
19
+ $notifications = self::get_notifications();
20
+ if (!in_array($scan->id, $notifications)) {
21
+
22
+ // Add scan to notifications
23
+ $notifications[] = $scan->id;
24
+ self::set_notifications($notifications);
25
+
26
+ // Spawn admin ajax URL
27
+ wplnst_require('core', 'curl');
28
+ WPLNST_Core_CURL::spawn(array('CURLOPT_URL' => add_query_arg(array(
29
+ 'wplnst_notify_email' => 'on',
30
+ 'wplnst_notify_nonce' => WPLNST_Core_Alive::get_notify_nonce(),
31
+ ), rtrim(admin_url('admin-ajax.php'), '/'))));
32
+ }
33
+ }
34
+
35
+
36
+
37
+ /**
38
+ * Check for pending notifications
39
+ */
40
+ public static function check() {
41
+
42
+ // Retrieve notifications
43
+ $notifications = self::get_notifications();
44
+ if (empty($notifications))
45
+ return;
46
+
47
+ // Extract first element
48
+ $scan_id = (int) array_shift($notifications);
49
+ self::set_notifications($notifications);
50
+
51
+ // Check identifier
52
+ if (!$scan_id > 0)
53
+ return;
54
+
55
+ // Check scan
56
+ global $wpdb;
57
+ $scan_row = $wpdb->get_row($wpdb->prepare('SELECT name, config FROM '.$wpdb->prefix.'wplnst_scans WHERE scan_id = %d', $scan_id));
58
+ if (empty($scan_row) || !is_object($scan_row))
59
+ return;
60
+
61
+ // Extract configuration
62
+ $config = @json_decode($scan_row->config, true);
63
+ if (empty($config) || !is_array($config))
64
+ return;
65
+
66
+ // Initialize
67
+ $emails = array();
68
+
69
+ // Default e-mail
70
+ if (isset($config['notify_default']) && true === $config['notify_default']) {
71
+ $admin_email = get_option('admin_email');
72
+ if (!empty($admin_email) && is_email($admin_email))
73
+ $emails[] = $admin_email;
74
+ }
75
+
76
+ // New addresses
77
+ if (isset($config['notify_address']) && true === $config['notify_address'] && !empty($config['notify_address_email'])) {
78
+ $addresses = str_replace(',', ';', $config['notify_address_email']);
79
+ $addresses = explode(';', $addresses);
80
+ foreach ($addresses as $address) {
81
+ if (!empty($address) && is_email($address))
82
+ $emails[] = $address;
83
+ }
84
+ }
85
+
86
+ // Check collection
87
+ if (empty($emails))
88
+ return;
89
+
90
+ // Send
91
+ $result = wp_mail(array_unique($emails), __('WP Link Status scan completed', 'wplnst'),
92
+ sprintf(__('
93
+
94
+ Your scan is completed, you can see the results here:
95
+
96
+ %s
97
+ %s
98
+
99
+ ', 'wplnst'), empty($scan_row->name)? __('(no name)', 'wplnst') : $scan_row->name, WPLNST_Core_Plugin::get_url_scans_results($scan_id)));
100
+ }
101
+
102
+
103
+
104
+ /**
105
+ * Retrieve option with scans to notify
106
+ */
107
+ private static function get_notifications() {
108
+ $notifications = @json_decode(get_option('wplnst_notifications'), true);
109
+ return (empty($notifications) || !is_array($notifications))? array() : $notifications;
110
+ }
111
+
112
+
113
+
114
+ /**
115
+ * Save notifications
116
+ */
117
+ private static function set_notifications($notifications) {
118
+ return update_option('wplnst_notifications', @json_encode($notifications));
119
+ }
120
+
121
+
122
+
123
+ }
core/plugin.php ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * WP Link Status Core Plugin class
5
+ *
6
+ * @package WP Link Status
7
+ * @subpackage WP Link Status Core
8
+ */
9
+ class WPLNST_Core_Plugin {
10
+
11
+
12
+
13
+ // Constants
14
+ // ---------------------------------------------------------------------------------------------------
15
+
16
+
17
+
18
+ /**
19
+ * Plugin
20
+ */
21
+ const title = 'WP Link Status';
22
+ const slug = 'wp-link-status';
23
+ const capability = 'manage_options';
24
+
25
+
26
+
27
+ // Compose sections URLs
28
+ // ---------------------------------------------------------------------------------------------------
29
+
30
+
31
+
32
+ /**
33
+ * Retrieves scan base admin URL or extended
34
+ */
35
+ public static function get_url_scans() {
36
+ return admin_url('admin.php?page='.self::slug);
37
+ }
38
+
39
+
40
+
41
+ /**
42
+ * URL for new scan
43
+ */
44
+ public static function get_url_scans_add() {
45
+ return self::get_url_scans().'-new-scan';
46
+ }
47
+
48
+
49
+
50
+ /**
51
+ * URL to edit a scan
52
+ */
53
+ public static function get_url_scans_edit($scan_id, $updated = false, $started = false) {
54
+ return self::get_url_scans().'&scan_id='.$scan_id.'&context=edit'.($updated? '&updated=on'.((false !== $started)? '&started='.$started : '') : '');
55
+ }
56
+
57
+
58
+
59
+ /**
60
+ * URL to start/stop scan
61
+ */
62
+ public static function get_url_scans_crawler($scan_id, $operation, $hash) {
63
+ return self::get_url_scans().'&scan_id='.$scan_id.'&context=crawler&operation='.$operation.'&nonce='.wp_create_nonce('scan-crawler-'.$hash);
64
+ }
65
+
66
+
67
+
68
+ /**
69
+ * URL to show scan results
70
+ */
71
+ public static function get_url_scans_results($scan_id) {
72
+ return self::get_url_scans().'&scan_id='.$scan_id.'&context=results';
73
+ }
74
+
75
+
76
+
77
+ /**
78
+ * URL to remove scan
79
+ */
80
+ public static function get_url_scans_delete($scan_id, $hash) {
81
+ return self::get_url_scans().'&scan_id='.$scan_id.'&context=delete&nonce='.wp_create_nonce($hash);
82
+ }
83
+
84
+
85
+
86
+ /**
87
+ * URL for settings page
88
+ */
89
+ public static function get_url_settings() {
90
+ return self::get_url_scans().'-settings';
91
+ }
92
+
93
+
94
+
95
+ /**
96
+ * URL for extensions page
97
+ */
98
+ public static function get_url_extensions() {
99
+ return self::get_url_scans().'-extensions';
100
+ }
101
+
102
+
103
+
104
+ // Other plugin methods
105
+ // ---------------------------------------------------------------------------------------------------
106
+
107
+
108
+
109
+ /**
110
+ * Load translation file
111
+ */
112
+ public static function load_plugin_textdomain($lang_dir = '') {
113
+ load_plugin_textdomain('wplnst', false, basename(WPLNST_PATH).'/'.(empty($lang_dir)? 'languages' : $lang_dir));
114
+ }
115
+
116
+
117
+
118
+ }
core/register.php ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * WP Link Status Core Register class
5
+ *
6
+ * @package WP Link Status
7
+ * @subpackage WP Link Status Core
8
+ */
9
+ class WPLNST_Core_Register {
10
+
11
+
12
+
13
+ /**
14
+ * Plugin activation process
15
+ */
16
+ public static function activation() {
17
+
18
+ // Check salt file
19
+ WPLNST_Core_Nonce::check_salt_file();
20
+
21
+ // Load scheme library
22
+ wplnst_require('core', 'scheme');
23
+
24
+ // Check plugins tables
25
+ if (false !== ($tables = WPLNST_Core_Scheme::check_tables()))
26
+ WPLNST_Core_Scheme::create_tables($tables);
27
+
28
+ // Check scheme upgrade
29
+ WPLNST_Core_Scheme::upgrade();
30
+ }
31
+
32
+
33
+
34
+ /**
35
+ * Plugin deactivation process
36
+ */
37
+ public static function deactivation() {
38
+ WPLNST_Core_Settings::delete_crawler_options();
39
+ }
40
+
41
+
42
+
43
+ /**
44
+ * Uninstall plugin data
45
+ */
46
+ public static function uninstall() {
47
+
48
+ // Uninstall info first
49
+ if (!wplnst_get_bsetting('uninstall_data'))
50
+ return;
51
+
52
+ // Remove salt file
53
+ WPLNST_Core_Nonce::remove_salt_file();
54
+
55
+ // Remove user meta
56
+ $user_id = get_current_user_id();
57
+ delete_user_meta($user_id, 'wplnst_advanced_search');
58
+ delete_user_meta($user_id, 'wplnst_scans_per_page');
59
+ delete_user_meta($user_id, 'wplnst_scan_results_per_page');
60
+
61
+ // Remove plugin options
62
+ WPLNST_Core_Settings::delete_all_options();
63
+
64
+ // Remove scheme tables
65
+ wplnst_require('core', 'scheme');
66
+ WPLNST_Core_Scheme::drop_tables();
67
+ }
68
+
69
+
70
+
71
+ }
core/requests/class-json.php ADDED
@@ -0,0 +1,960 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ if ( ! class_exists( 'Services_JSON' ) ) :
3
+ /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
4
+ /**
5
+ * Converts to and from JSON format.
6
+ *
7
+ * JSON (JavaScript Object Notation) is a lightweight data-interchange
8
+ * format. It is easy for humans to read and write. It is easy for machines
9
+ * to parse and generate. It is based on a subset of the JavaScript
10
+ * Programming Language, Standard ECMA-262 3rd Edition - December 1999.
11
+ * This feature can also be found in Python. JSON is a text format that is
12
+ * completely language independent but uses conventions that are familiar
13
+ * to programmers of the C-family of languages, including C, C++, C#, Java,
14
+ * JavaScript, Perl, TCL, and many others. These properties make JSON an
15
+ * ideal data-interchange language.
16
+ *
17
+ * This package provides a simple encoder and decoder for JSON notation. It
18
+ * is intended for use with client-side Javascript applications that make
19
+ * use of HTTPRequest to perform server communication functions - data can
20
+ * be encoded into JSON notation for use in a client-side javascript, or
21
+ * decoded from incoming Javascript requests. JSON format is native to
22
+ * Javascript, and can be directly eval()'ed with no further parsing
23
+ * overhead
24
+ *
25
+ * All strings should be in ASCII or UTF-8 format!
26
+ *
27
+ * LICENSE: Redistribution and use in source and binary forms, with or
28
+ * without modification, are permitted provided that the following
29
+ * conditions are met: Redistributions of source code must retain the
30
+ * above copyright notice, this list of conditions and the following
31
+ * disclaimer. Redistributions in binary form must reproduce the above
32
+ * copyright notice, this list of conditions and the following disclaimer
33
+ * in the documentation and/or other materials provided with the
34
+ * distribution.
35
+ *
36
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
37
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
38
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
39
+ * NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
40
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
41
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
42
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
43
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
44
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
45
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
46
+ * DAMAGE.
47
+ *
48
+ * @category
49
+ * @package Services_JSON
50
+ * @author Michal Migurski <mike-json@teczno.com>
51
+ * @author Matt Knapp <mdknapp[at]gmail[dot]com>
52
+ * @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
53
+ * @copyright 2005 Michal Migurski
54
+ * @version CVS: $Id: JSON.php 305040 2010-11-02 23:19:03Z alan_k $
55
+ * @license http://www.opensource.org/licenses/bsd-license.php
56
+ * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198
57
+ */
58
+
59
+ /**
60
+ * Marker constant for Services_JSON::decode(), used to flag stack state
61
+ */
62
+ define('SERVICES_JSON_SLICE', 1);
63
+
64
+ /**
65
+ * Marker constant for Services_JSON::decode(), used to flag stack state
66
+ */
67
+ define('SERVICES_JSON_IN_STR', 2);
68
+
69
+ /**
70
+ * Marker constant for Services_JSON::decode(), used to flag stack state
71
+ */
72
+ define('SERVICES_JSON_IN_ARR', 3);
73
+
74
+ /**
75
+ * Marker constant for Services_JSON::decode(), used to flag stack state
76
+ */
77
+ define('SERVICES_JSON_IN_OBJ', 4);
78
+
79
+ /**
80
+ * Marker constant for Services_JSON::decode(), used to flag stack state
81
+ */
82
+ define('SERVICES_JSON_IN_CMT', 5);
83
+
84
+ /**
85
+ * Behavior switch for Services_JSON::decode()
86
+ */
87
+ define('SERVICES_JSON_LOOSE_TYPE', 16);
88
+
89
+ /**
90
+ * Behavior switch for Services_JSON::decode()
91
+ */
92
+ define('SERVICES_JSON_SUPPRESS_ERRORS', 32);
93
+
94
+ /**
95
+ * Behavior switch for Services_JSON::decode()
96
+ */
97
+ define('SERVICES_JSON_USE_TO_JSON', 64);
98
+
99
+ /**
100
+ * Converts to and from JSON format.
101
+ *
102
+ * Brief example of use:
103
+ *
104
+ * <code>
105
+ * // create a new instance of Services_JSON
106
+ * $json = new Services_JSON();
107
+ *
108
+ * // convert a complexe value to JSON notation, and send it to the browser
109
+ * $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4)));
110
+ * $output = $json->encode($value);
111
+ *
112
+ * print($output);
113
+ * // prints: ["foo","bar",[1,2,"baz"],[3,[4]]]
114
+ *
115
+ * // accept incoming POST data, assumed to be in JSON notation
116
+ * $input = file_get_contents('php://input', 1000000);
117
+ * $value = $json->decode($input);
118
+ * </code>
119
+ */
120
+ class Services_JSON
121
+ {
122
+ /**
123
+ * constructs a new JSON instance
124
+ *
125
+ * @param int $use object behavior flags; combine with boolean-OR
126
+ *
127
+ * possible values:
128
+ * - SERVICES_JSON_LOOSE_TYPE: loose typing.
129
+ * "{...}" syntax creates associative arrays
130
+ * instead of objects in decode().
131
+ * - SERVICES_JSON_SUPPRESS_ERRORS: error suppression.
132
+ * Values which can't be encoded (e.g. resources)
133
+ * appear as NULL instead of throwing errors.
134
+ * By default, a deeply-nested resource will
135
+ * bubble up with an error, so all return values
136
+ * from encode() should be checked with isError()
137
+ * - SERVICES_JSON_USE_TO_JSON: call toJSON when serializing objects
138
+ * It serializes the return value from the toJSON call rather
139
+ * than the object itself, toJSON can return associative arrays,
140
+ * strings or numbers, if you return an object, make sure it does
141
+ * not have a toJSON method, otherwise an error will occur.
142
+ */
143
+ function __construct( $use = 0 )
144
+ {
145
+ $this->use = $use;
146
+ $this->_mb_strlen = function_exists('mb_strlen');
147
+ $this->_mb_convert_encoding = function_exists('mb_convert_encoding');
148
+ $this->_mb_substr = function_exists('mb_substr');
149
+ }
150
+
151
+ /**
152
+ * PHP4 constructor.
153
+ */
154
+ public function Services_JSON( $use = 0 ) {
155
+ self::__construct( $use );
156
+ }
157
+ // private - cache the mbstring lookup results..
158
+ var $_mb_strlen = false;
159
+ var $_mb_substr = false;
160
+ var $_mb_convert_encoding = false;
161
+
162
+ /**
163
+ * convert a string from one UTF-16 char to one UTF-8 char
164
+ *
165
+ * Normally should be handled by mb_convert_encoding, but
166
+ * provides a slower PHP-only method for installations
167
+ * that lack the multibye string extension.
168
+ *
169
+ * @param string $utf16 UTF-16 character
170
+ * @return string UTF-8 character
171
+ * @access private
172
+ */
173
+ function utf162utf8($utf16)
174
+ {
175
+ // oh please oh please oh please oh please oh please
176
+ if($this->_mb_convert_encoding) {
177
+ return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');
178
+ }
179
+
180
+ $bytes = (ord($utf16{0}) << 8) | ord($utf16{1});
181
+
182
+ switch(true) {
183
+ case ((0x7F & $bytes) == $bytes):
184
+ // this case should never be reached, because we are in ASCII range
185
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
186
+ return chr(0x7F & $bytes);
187
+
188
+ case (0x07FF & $bytes) == $bytes:
189
+ // return a 2-byte UTF-8 character
190
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
191
+ return chr(0xC0 | (($bytes >> 6) & 0x1F))
192
+ . chr(0x80 | ($bytes & 0x3F));
193
+
194
+ case (0xFFFF & $bytes) == $bytes:
195
+ // return a 3-byte UTF-8 character
196
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
197
+ return chr(0xE0 | (($bytes >> 12) & 0x0F))
198
+ . chr(0x80 | (($bytes >> 6) & 0x3F))
199
+ . chr(0x80 | ($bytes & 0x3F));
200
+ }
201
+
202
+ // ignoring UTF-32 for now, sorry
203
+ return '';
204
+ }
205
+
206
+ /**
207
+ * convert a string from one UTF-8 char to one UTF-16 char
208
+ *
209
+ * Normally should be handled by mb_convert_encoding, but
210
+ * provides a slower PHP-only method for installations
211
+ * that lack the multibye string extension.
212
+ *
213
+ * @param string $utf8 UTF-8 character
214
+ * @return string UTF-16 character
215
+ * @access private
216
+ */
217
+ function utf82utf16($utf8)
218
+ {
219
+ // oh please oh please oh please oh please oh please
220
+ if($this->_mb_convert_encoding) {
221
+ return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
222
+ }
223
+
224
+ switch($this->strlen8($utf8)) {
225
+ case 1:
226
+ // this case should never be reached, because we are in ASCII range
227
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
228
+ return $utf8;
229
+
230
+ case 2:
231
+ // return a UTF-16 character from a 2-byte UTF-8 char
232
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
233
+ return chr(0x07 & (ord($utf8{0}) >> 2))
234
+ . chr((0xC0 & (ord($utf8{0}) << 6))
235
+ | (0x3F & ord($utf8{1})));
236
+
237
+ case 3:
238
+ // return a UTF-16 character from a 3-byte UTF-8 char
239
+ // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
240
+ return chr((0xF0 & (ord($utf8{0}) << 4))
241
+ | (0x0F & (ord($utf8{1}) >> 2)))
242
+ . chr((0xC0 & (ord($utf8{1}) << 6))
243
+ | (0x7F & ord($utf8{2})));
244
+ }
245
+
246
+ // ignoring UTF-32 for now, sorry
247
+ return '';
248
+ }
249
+
250
+ /**
251
+ * encodes an arbitrary variable into JSON format (and sends JSON Header)
252
+ *
253
+ * @param mixed $var any number, boolean, string, array, or object to be encoded.
254
+ * see argument 1 to Services_JSON() above for array-parsing behavior.
255
+ * if var is a strng, note that encode() always expects it
256
+ * to be in ASCII or UTF-8 format!
257
+ *
258
+ * @return mixed JSON string representation of input var or an error if a problem occurs
259
+ * @access public
260
+ */
261
+ function encode($var)
262
+ {
263
+ header('Content-type: application/json');
264
+ return $this->encodeUnsafe($var);
265
+ }
266
+ /**
267
+ * encodes an arbitrary variable into JSON format without JSON Header - warning - may allow XSS!!!!)
268
+ *
269
+ * @param mixed $var any number, boolean, string, array, or object to be encoded.
270
+ * see argument 1 to Services_JSON() above for array-parsing behavior.
271
+ * if var is a strng, note that encode() always expects it
272
+ * to be in ASCII or UTF-8 format!
273
+ *
274
+ * @return mixed JSON string representation of input var or an error if a problem occurs
275
+ * @access public
276
+ */
277
+ function encodeUnsafe($var)
278
+ {
279
+ // see bug #16908 - regarding numeric locale printing
280
+ $lc = setlocale(LC_NUMERIC, 0);
281
+ setlocale(LC_NUMERIC, 'C');
282
+ $ret = $this->_encode($var);
283
+ setlocale(LC_NUMERIC, $lc);
284
+ return $ret;
285
+
286
+ }
287
+ /**
288
+ * PRIVATE CODE that does the work of encodes an arbitrary variable into JSON format
289
+ *
290
+ * @param mixed $var any number, boolean, string, array, or object to be encoded.
291
+ * see argument 1 to Services_JSON() above for array-parsing behavior.
292
+ * if var is a strng, note that encode() always expects it
293
+ * to be in ASCII or UTF-8 format!
294
+ *
295
+ * @return mixed JSON string representation of input var or an error if a problem occurs
296
+ * @access public
297
+ */
298
+ function _encode($var)
299
+ {
300
+
301
+ switch (gettype($var)) {
302
+ case 'boolean':
303
+ return $var ? 'true' : 'false';
304
+
305
+ case 'NULL':
306
+ return 'null';
307
+
308
+ case 'integer':
309
+ return (int) $var;
310
+
311
+ case 'double':
312
+ case 'float':
313
+ return (float) $var;
314
+
315
+ case 'string':
316
+ // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
317
+ $ascii = '';
318
+ $strlen_var = $this->strlen8($var);
319
+
320
+ /*
321
+ * Iterate over every character in the string,
322
+ * escaping with a slash or encoding to UTF-8 where necessary
323
+ */
324
+ for ($c = 0; $c < $strlen_var; ++$c) {
325
+
326
+ $ord_var_c = ord($var{$c});
327
+
328
+ switch (true) {
329
+ case $ord_var_c == 0x08:
330
+ $ascii .= '\b';
331
+ break;
332
+ case $ord_var_c == 0x09:
333
+ $ascii .= '\t';
334
+ break;
335
+ case $ord_var_c == 0x0A:
336
+ $ascii .= '\n';
337
+ break;
338
+ case $ord_var_c == 0x0C:
339
+ $ascii .= '\f';
340
+ break;
341
+ case $ord_var_c == 0x0D:
342
+ $ascii .= '\r';
343
+ break;
344
+
345
+ case $ord_var_c == 0x22:
346
+ case $ord_var_c == 0x2F:
347
+ case $ord_var_c == 0x5C:
348
+ // double quote, slash, slosh
349
+ $ascii .= '\\'.$var{$c};
350
+ break;
351
+
352
+ case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
353
+ // characters U-00000000 - U-0000007F (same as ASCII)
354
+ $ascii .= $var{$c};
355
+ break;
356
+
357
+ case (($ord_var_c & 0xE0) == 0xC0):
358
+ // characters U-00000080 - U-000007FF, mask 110XXXXX
359
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
360
+ if ($c+1 >= $strlen_var) {
361
+ $c += 1;
362
+ $ascii .= '?';
363
+ break;
364
+ }
365
+
366
+ $char = pack('C*', $ord_var_c, ord($var{$c + 1}));
367
+ $c += 1;
368
+ $utf16 = $this->utf82utf16($char);
369
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
370
+ break;
371
+
372
+ case (($ord_var_c & 0xF0) == 0xE0):
373
+ if ($c+2 >= $strlen_var) {
374
+ $c += 2;
375
+ $ascii .= '?';
376
+ break;
377
+ }
378
+ // characters U-00000800 - U-0000FFFF, mask 1110XXXX
379
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
380
+ $char = pack('C*', $ord_var_c,
381
+ @ord($var{$c + 1}),
382
+ @ord($var{$c + 2}));
383
+ $c += 2;
384
+ $utf16 = $this->utf82utf16($char);
385
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
386
+ break;
387
+
388
+ case (($ord_var_c & 0xF8) == 0xF0):
389
+ if ($c+3 >= $strlen_var) {
390
+ $c += 3;
391
+ $ascii .= '?';
392
+ break;
393
+ }
394
+ // characters U-00010000 - U-001FFFFF, mask 11110XXX
395
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
396
+ $char = pack('C*', $ord_var_c,
397
+ ord($var{$c + 1}),
398
+ ord($var{$c + 2}),
399
+ ord($var{$c + 3}));
400
+ $c += 3;
401
+ $utf16 = $this->utf82utf16($char);
402
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
403
+ break;
404
+
405
+ case (($ord_var_c & 0xFC) == 0xF8):
406
+ // characters U-00200000 - U-03FFFFFF, mask 111110XX
407
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
408
+ if ($c+4 >= $strlen_var) {
409
+ $c += 4;
410
+ $ascii .= '?';
411
+ break;
412
+ }
413
+ $char = pack('C*', $ord_var_c,
414
+ ord($var{$c + 1}),
415
+ ord($var{$c + 2}),
416
+ ord($var{$c + 3}),
417
+ ord($var{$c + 4}));
418
+ $c += 4;
419
+ $utf16 = $this->utf82utf16($char);
420
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
421
+ break;
422
+
423
+ case (($ord_var_c & 0xFE) == 0xFC):
424
+ if ($c+5 >= $strlen_var) {
425
+ $c += 5;
426
+ $ascii .= '?';
427
+ break;
428
+ }
429
+ // characters U-04000000 - U-7FFFFFFF, mask 1111110X
430
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
431
+ $char = pack('C*', $ord_var_c,
432
+ ord($var{$c + 1}),
433
+ ord($var{$c + 2}),
434
+ ord($var{$c + 3}),
435
+ ord($var{$c + 4}),
436
+ ord($var{$c + 5}));
437
+ $c += 5;
438
+ $utf16 = $this->utf82utf16($char);
439
+ $ascii .= sprintf('\u%04s', bin2hex($utf16));
440
+ break;
441
+ }
442
+ }
443
+ return '"'.$ascii.'"';
444
+
445
+ case 'array':
446
+ /*
447
+ * As per JSON spec if any array key is not an integer
448
+ * we must treat the the whole array as an object. We
449
+ * also try to catch a sparsely populated associative
450
+ * array with numeric keys here because some JS engines
451
+ * will create an array with empty indexes up to
452
+ * max_index which can cause memory issues and because
453
+ * the keys, which may be relevant, will be remapped
454
+ * otherwise.
455
+ *
456
+ * As per the ECMA and JSON specification an object may
457
+ * have any string as a property. Unfortunately due to
458
+ * a hole in the ECMA specification if the key is a
459
+ * ECMA reserved word or starts with a digit the
460
+ * parameter is only accessible using ECMAScript's
461
+ * bracket notation.
462
+ */
463
+
464
+ // treat as a JSON object
465
+ if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
466
+ $properties = array_map(array($this, 'name_value'),
467
+ array_keys($var),
468
+ array_values($var));
469
+
470
+ foreach($properties as $property) {
471
+ if(Services_JSON::isError($property)) {
472
+ return $property;
473
+ }
474
+ }
475
+
476
+ return '{' . join(',', $properties) . '}';
477
+ }
478
+
479
+ // treat it like a regular array
480
+ $elements = array_map(array($this, '_encode'), $var);
481
+
482
+ foreach($elements as $element) {
483
+ if(Services_JSON::isError($element)) {
484
+ return $element;
485
+ }
486
+ }
487
+
488
+ return '[' . join(',', $elements) . ']';
489
+
490
+ case 'object':
491
+
492
+ // support toJSON methods.
493
+ if (($this->use & SERVICES_JSON_USE_TO_JSON) && method_exists($var, 'toJSON')) {
494
+ // this may end up allowing unlimited recursion
495
+ // so we check the return value to make sure it's not got the same method.
496
+ $recode = $var->toJSON();
497
+
498
+ if (method_exists($recode, 'toJSON')) {
499
+
500
+ return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS)
501
+ ? 'null'
502
+ : new Services_JSON_Error(get_class($var).
503
+ " toJSON returned an object with a toJSON method.");
504
+
505
+ }
506
+
507
+ return $this->_encode( $recode );
508
+ }
509
+
510
+ $vars = get_object_vars($var);
511
+
512
+ $properties = array_map(array($this, 'name_value'),
513
+ array_keys($vars),
514
+ array_values($vars));
515
+
516
+ foreach($properties as $property) {
517
+ if(Services_JSON::isError($property)) {
518
+ return $property;
519
+ }
520
+ }
521
+
522
+ return '{' . join(',', $properties) . '}';
523
+
524
+ default:
525
+ return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS)
526
+ ? 'null'
527
+ : new Services_JSON_Error(gettype($var)." can not be encoded as JSON string");
528
+ }
529
+ }
530
+
531
+ /**
532
+ * array-walking function for use in generating JSON-formatted name-value pairs
533
+ *
534
+ * @param string $name name of key to use
535
+ * @param mixed $value reference to an array element to be encoded
536
+ *
537
+ * @return string JSON-formatted name-value pair, like '"name":value'
538
+ * @access private
539
+ */
540
+ function name_value($name, $value)
541
+ {
542
+ $encoded_value = $this->_encode($value);
543
+
544
+ if(Services_JSON::isError($encoded_value)) {
545
+ return $encoded_value;
546
+ }
547
+
548
+ return $this->_encode(strval($name)) . ':' . $encoded_value;
549
+ }
550
+
551
+ /**
552
+ * reduce a string by removing leading and trailing comments and whitespace
553
+ *
554
+ * @param $str string string value to strip of comments and whitespace
555
+ *
556
+ * @return string string value stripped of comments and whitespace
557
+ * @access private
558
+ */
559
+ function reduce_string($str)
560
+ {
561
+ $str = preg_replace(array(
562
+
563
+ // eliminate single line comments in '// ...' form
564
+ '#^\s*//(.+)$#m',
565
+
566
+ // eliminate multi-line comments in '/* ... */' form, at start of string
567
+ '#^\s*/\*(.+)\*/#Us',
568
+
569
+ // eliminate multi-line comments in '/* ... */' form, at end of string
570
+ '#/\*(.+)\*/\s*$#Us'
571
+
572
+ ), '', $str);
573
+
574
+ // eliminate extraneous space
575
+ return trim($str);
576
+ }
577
+
578
+ /**
579
+ * decodes a JSON string into appropriate variable
580
+ *
581
+ * @param string $str JSON-formatted string
582
+ *
583
+ * @return mixed number, boolean, string, array, or object
584
+ * corresponding to given JSON input string.
585
+ * See argument 1 to Services_JSON() above for object-output behavior.
586
+ * Note that decode() always returns strings
587
+ * in ASCII or UTF-8 format!
588
+ * @access public
589
+ */
590
+ function decode($str)
591
+ {
592
+ $str = $this->reduce_string($str);
593
+
594
+ switch (strtolower($str)) {
595
+ case 'true':
596
+ return true;
597
+
598
+ case 'false':
599
+ return false;
600
+
601
+ case 'null':
602
+ return null;
603
+
604
+ default:
605
+ $m = array();
606
+
607
+ if (is_numeric($str)) {
608
+ // Lookie-loo, it's a number
609
+
610
+ // This would work on its own, but I'm trying to be
611
+ // good about returning integers where appropriate:
612
+ // return (float)$str;
613
+
614
+ // Return float or int, as appropriate
615
+ return ((float)$str == (integer)$str)
616
+ ? (integer)$str
617
+ : (float)$str;
618
+
619
+ } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) {
620
+ // STRINGS RETURNED IN UTF-8 FORMAT
621
+ $delim = $this->substr8($str, 0, 1);
622
+ $chrs = $this->substr8($str, 1, -1);
623
+ $utf8 = '';
624
+ $strlen_chrs = $this->strlen8($chrs);
625
+
626
+ for ($c = 0; $c < $strlen_chrs; ++$c) {
627
+
628
+ $substr_chrs_c_2 = $this->substr8($chrs, $c, 2);
629
+ $ord_chrs_c = ord($chrs{$c});
630
+
631
+ switch (true) {
632
+ case $substr_chrs_c_2 == '\b':
633
+ $utf8 .= chr(0x08);
634
+ ++$c;
635
+ break;
636
+ case $substr_chrs_c_2 == '\t':
637
+ $utf8 .= chr(0x09);
638
+ ++$c;
639
+ break;
640
+ case $substr_chrs_c_2 == '\n':
641
+ $utf8 .= chr(0x0A);
642
+ ++$c;
643
+ break;
644
+ case $substr_chrs_c_2 == '\f':
645
+ $utf8 .= chr(0x0C);
646
+ ++$c;
647
+ break;
648
+ case $substr_chrs_c_2 == '\r':
649
+ $utf8 .= chr(0x0D);
650
+ ++$c;
651
+ break;
652
+
653
+ case $substr_chrs_c_2 == '\\"':
654
+ case $substr_chrs_c_2 == '\\\'':
655
+ case $substr_chrs_c_2 == '\\\\':
656
+ case $substr_chrs_c_2 == '\\/':
657
+ if (($delim == '"' && $substr_chrs_c_2 != '\\\'') ||
658
+ ($delim == "'" && $substr_chrs_c_2 != '\\"')) {
659
+ $utf8 .= $chrs{++$c};
660
+ }
661
+ break;
662
+
663
+ case preg_match('/\\\u[0-9A-F]{4}/i', $this->substr8($chrs, $c, 6)):
664
+ // single, escaped unicode character
665
+ $utf16 = chr(hexdec($this->substr8($chrs, ($c + 2), 2)))
666
+ . chr(hexdec($this->substr8($chrs, ($c + 4), 2)));
667
+ $utf8 .= $this->utf162utf8($utf16);
668
+ $c += 5;
669
+ break;
670
+
671
+ case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F):
672
+ $utf8 .= $chrs{$c};
673
+ break;
674
+
675
+ case ($ord_chrs_c & 0xE0) == 0xC0:
676
+ // characters U-00000080 - U-000007FF, mask 110XXXXX
677
+ //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
678
+ $utf8 .= $this->substr8($chrs, $c, 2);
679
+ ++$c;
680
+ break;
681
+
682
+ case ($ord_chrs_c & 0xF0) == 0xE0:
683
+ // characters U-00000800 - U-0000FFFF, mask 1110XXXX
684
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
685
+ $utf8 .= $this->substr8($chrs, $c, 3);
686
+ $c += 2;
687
+ break;
688
+
689
+ case ($ord_chrs_c & 0xF8) == 0xF0:
690
+ // characters U-00010000 - U-001FFFFF, mask 11110XXX
691
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
692
+ $utf8 .= $this->substr8($chrs, $c, 4);
693
+ $c += 3;
694
+ break;
695
+
696
+ case ($ord_chrs_c & 0xFC) == 0xF8:
697
+ // characters U-00200000 - U-03FFFFFF, mask 111110XX
698
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
699
+ $utf8 .= $this->substr8($chrs, $c, 5);
700
+ $c += 4;
701
+ break;
702
+
703
+ case ($ord_chrs_c & 0xFE) == 0xFC:
704
+ // characters U-04000000 - U-7FFFFFFF, mask 1111110X
705
+ // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
706
+ $utf8 .= $this->substr8($chrs, $c, 6);
707
+ $c += 5;
708
+ break;
709
+
710
+ }
711
+
712
+ }
713
+
714
+ return $utf8;
715
+
716
+ } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) {
717
+ // array, or object notation
718
+
719
+ if ($str{0} == '[') {
720
+ $stk = array(SERVICES_JSON_IN_ARR);
721
+ $arr = array();
722
+ } else {
723
+ if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
724
+ $stk = array(SERVICES_JSON_IN_OBJ);
725
+ $obj = array();
726
+ } else {
727
+ $stk = array(SERVICES_JSON_IN_OBJ);
728
+ $obj = new stdClass();
729
+ }
730
+ }
731
+
732
+ array_push($stk, array('what' => SERVICES_JSON_SLICE,
733
+ 'where' => 0,
734
+ 'delim' => false));
735
+
736
+ $chrs = $this->substr8($str, 1, -1);
737
+ $chrs = $this->reduce_string($chrs);
738
+
739
+ if ($chrs == '') {
740
+ if (reset($stk) == SERVICES_JSON_IN_ARR) {
741
+ return $arr;
742
+
743
+ } else {
744
+ return $obj;
745
+
746
+ }
747
+ }
748
+
749
+ //print("\nparsing {$chrs}\n");
750
+
751
+ $strlen_chrs = $this->strlen8($chrs);
752
+
753
+ for ($c = 0; $c <= $strlen_chrs; ++$c) {
754
+
755
+ $top = end($stk);
756
+ $substr_chrs_c_2 = $this->substr8($chrs, $c, 2);
757
+
758
+ if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) {
759
+ // found a comma that is not inside a string, array, etc.,
760
+ // OR we've reached the end of the character list
761
+ $slice = $this->substr8($chrs, $top['where'], ($c - $top['where']));
762
+ array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false));
763
+ //print("Found split at {$c}: ".$this->substr8($chrs, $top['where'], (1 + $c - $top['where']))."\n");
764
+
765
+ if (reset($stk) == SERVICES_JSON_IN_ARR) {
766
+ // we are in an array, so just push an element onto the stack
767
+ array_push($arr, $this->decode($slice));
768
+
769
+ } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
770
+ // we are in an object, so figure
771
+ // out the property name and set an
772
+ // element in an associative array,
773
+ // for now
774
+ $parts = array();
775
+
776
+ if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:/Uis', $slice, $parts)) {
777
+ // "name":value pair
778
+ $key = $this->decode($parts[1]);
779
+ $val = $this->decode(trim(substr($slice, strlen($parts[0])), ", \t\n\r\0\x0B"));
780
+ if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
781
+ $obj[$key] = $val;
782
+ } else {
783
+ $obj->$key = $val;
784
+ }
785
+ } elseif (preg_match('/^\s*(\w+)\s*:/Uis', $slice, $parts)) {
786
+ // name:value pair, where name is unquoted
787
+ $key = $parts[1];
788
+ $val = $this->decode(trim(substr($slice, strlen($parts[0])), ", \t\n\r\0\x0B"));
789
+
790
+ if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
791
+ $obj[$key] = $val;
792
+ } else {
793
+ $obj->$key = $val;
794
+ }
795
+ }
796
+
797
+ }
798
+
799
+ } elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) {
800
+ // found a quote, and we are not inside a string
801
+ array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c}));
802
+ //print("Found start of string at {$c}\n");
803
+
804
+ } elseif (($chrs{$c} == $top['delim']) &&
805
+ ($top['what'] == SERVICES_JSON_IN_STR) &&
806
+ (($this->strlen8($this->substr8($chrs, 0, $c)) - $this->strlen8(rtrim($this->substr8($chrs, 0, $c), '\\'))) % 2 != 1)) {
807
+ // found a quote, we're in a string, and it's not escaped
808
+ // we know that it's not escaped becase there is _not_ an
809
+ // odd number of backslashes at the end of the string so far
810
+ array_pop($stk);
811
+ //print("Found end of string at {$c}: ".$this->substr8($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n");
812
+
813
+ } elseif (($chrs{$c} == '[') &&
814
+ in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
815
+ // found a left-bracket, and we are in an array, object, or slice
816
+ array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false));
817
+ //print("Found start of array at {$c}\n");
818
+
819
+ } elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) {
820
+ // found a right-bracket, and we're in an array
821
+ array_pop($stk);
822
+ //print("Found end of array at {$c}: ".$this->substr8($chrs, $top['where'], (1 + $c - $top['where']))."\n");
823
+
824
+ } elseif (($chrs{$c} == '{') &&
825
+ in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
826
+ // found a left-brace, and we are in an array, object, or slice
827
+ array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false));
828
+ //print("Found start of object at {$c}\n");
829
+
830
+ } elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) {
831
+ // found a right-brace, and we're in an object
832
+ array_pop($stk);
833
+ //print("Found end of object at {$c}: ".$this->substr8($chrs, $top['where'], (1 + $c - $top['where']))."\n");
834
+
835
+ } elseif (($substr_chrs_c_2 == '/*') &&
836
+ in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
837
+ // found a comment start, and we are in an array, object, or slice
838
+ array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false));
839
+ $c++;
840
+ //print("Found start of comment at {$c}\n");
841
+
842
+ } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) {
843
+ // found a comment end, and we're in one now
844
+ array_pop($stk);
845
+ $c++;
846
+
847
+ for ($i = $top['where']; $i <= $c; ++$i)
848
+ $chrs = substr_replace($chrs, ' ', $i, 1);
849
+
850
+ //print("Found end of comment at {$c}: ".$this->substr8($chrs, $top['where'], (1 + $c - $top['where']))."\n");
851
+
852
+ }
853
+
854
+ }
855
+
856
+ if (reset($stk) == SERVICES_JSON_IN_ARR) {
857
+ return $arr;
858
+
859
+ } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
860
+ return $obj;
861
+
862
+ }
863
+
864
+ }
865
+ }
866
+ }
867
+
868
+ /**
869
+ * @todo Ultimately, this should just call PEAR::isError()
870
+ */
871
+ function isError($data, $code = null)
872
+ {
873
+ if (class_exists('pear')) {
874
+ return PEAR::isError($data, $code);
875
+ } elseif (is_object($data) && (get_class($data) == 'services_json_error' ||
876
+ is_subclass_of($data, 'services_json_error'))) {
877
+ return true;
878
+ }
879
+
880
+ return false;
881
+ }
882
+
883
+ /**
884
+ * Calculates length of string in bytes
885
+ * @param string
886
+ * @return integer length
887
+ */
888
+ function strlen8( $str )
889
+ {
890
+ if ( $this->_mb_strlen ) {
891
+ return mb_strlen( $str, "8bit" );
892
+ }
893
+ return strlen( $str );
894
+ }
895
+
896
+ /**
897
+ * Returns part of a string, interpreting $start and $length as number of bytes.
898
+ * @param string
899
+ * @param integer start
900
+ * @param integer length
901
+ * @return integer length
902
+ */
903
+ function substr8( $string, $start, $length=false )
904
+ {
905
+ if ( $length === false ) {
906
+ $length = $this->strlen8( $string ) - $start;
907
+ }
908
+ if ( $this->_mb_substr ) {
909
+ return mb_substr( $string, $start, $length, "8bit" );
910
+ }
911
+ return substr( $string, $start, $length );
912
+ }
913
+
914
+ }
915
+
916
+ if (class_exists('PEAR_Error')) {
917
+
918
+ class Services_JSON_Error extends PEAR_Error
919
+ {
920
+ function __construct($message = 'unknown error', $code = null,
921
+ $mode = null, $options = null, $userinfo = null)
922
+ {
923
+ parent::PEAR_Error($message, $code, $mode, $options, $userinfo);
924
+ }
925
+
926
+ public function Services_JSON_Error($message = 'unknown error', $code = null,
927
+ $mode = null, $options = null, $userinfo = null) {
928
+ self::__construct($message = 'unknown error', $code = null,
929
+ $mode = null, $options = null, $userinfo = null);
930
+ }
931
+ }
932
+
933
+ } else {
934
+
935
+ /**
936
+ * @todo Ultimately, this class shall be descended from PEAR_Error
937
+ */
938
+ class Services_JSON_Error
939
+ {
940
+ /**
941
+ * PHP5 constructor.
942
+ */
943
+ function __construct( $message = 'unknown error', $code = null,
944
+ $mode = null, $options = null, $userinfo = null )
945
+ {
946
+
947
+ }
948
+
949
+ /**
950
+ * PHP4 constructor.
951
+ */
952
+ public function Services_JSON_Error( $message = 'unknown error', $code = null,
953
+ $mode = null, $options = null, $userinfo = null ) {
954
+ self::__construct( $message, $code, $mode, $options, $userinfo );
955
+ }
956
+ }
957
+
958
+ }
959
+
960
+ endif;
core/requests/http.php ADDED
@@ -0,0 +1,417 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Check existing class to avoid conflicts
4
+ if (!class_exists('WPLNST_Core_HTTP_Request') && !function_exists('wplnst_http_read_stream')) :
5
+
6
+ /**
7
+ * WP Link Status Core HTTP request class
8
+ *
9
+ * @package WP Link Status
10
+ * @subpackage WP Link Status Core
11
+ */
12
+ class WPLNST_Core_HTTP_Request {
13
+
14
+
15
+
16
+ // Initialization
17
+ // ---------------------------------------------------------------------------------------------------
18
+
19
+
20
+
21
+ /**
22
+ * Start request process
23
+ */
24
+ public static function start() {
25
+
26
+
27
+
28
+ /* Initialization */
29
+
30
+ // Default timezone
31
+ date_default_timezone_set('UTC');
32
+
33
+ // Check basic arguments
34
+ if (!empty($_GET) || empty($_POST) || empty($_POST['url']) || empty($_POST['url_id']) || empty($_POST['connect_timeout']) || empty($_POST['request_timeout']) || empty($_POST['max_download']) || empty($_POST['nonce']) || empty($_POST['hash']))
35
+ self::terminate();
36
+
37
+ // Check URL
38
+ $url = $_POST['url'];
39
+ if (empty($url) || (0 !== stripos($url, 'http://') && 0 !== stripos($url, 'https://') && 0 !== stripos($url, 'ftp://')))
40
+ self::terminate();
41
+
42
+ // Check URL id
43
+ $url_id = (int) $_POST['url_id'];
44
+ if (empty($url_id))
45
+ self::terminate();
46
+
47
+ // Check back URL
48
+ $back_url = empty($_POST['back_url'])? false : trim($_POST['back_url']);
49
+ if (!empty($back_url) && (0 !== stripos($back_url, 'http://') && 0 !== stripos($back_url, 'https://')))
50
+ self::terminate();
51
+
52
+ // Check connect timeout
53
+ $connect_timeout = (int) $_POST['connect_timeout'];
54
+ if (empty($connect_timeout))
55
+ self::terminate();
56
+
57
+ // Check request timeout
58
+ $request_timeout = (int) $_POST['request_timeout'];
59
+ if (empty($request_timeout))
60
+ self::terminate();
61
+
62
+ // Check max download
63
+ global $max_download, $max_download_done;
64
+ $max_download = (int) $_POST['max_download'];
65
+ if (empty($max_download))
66
+ self::terminate();
67
+
68
+ // Check only headers
69
+ global $only_headers, $only_headers_done;
70
+ $only_headers = (isset($_POST['only_headers']) && '1' == $_POST['only_headers']);
71
+
72
+ // Load nonce library
73
+ require(dirname(dirname(__FILE__)).'/nonce/nonce.php');
74
+
75
+ // Verify nonce for this URL hash
76
+ if (!WPLNST_Core_Nonce::verify_nonce($_POST['nonce'], $_POST['hash']))
77
+ self::terminate();
78
+
79
+ // Load cURL wrapper library
80
+ require(dirname(dirname(__FILE__)).'/curl.php');
81
+
82
+ // User Agent string
83
+ $user_agent = empty($_POST['user_agent'])? '' : $_POST['user_agent'];
84
+
85
+
86
+
87
+ /* URL HTTP status */
88
+
89
+ // No timeout
90
+ set_time_limit(0);
91
+
92
+ // Initialize globals
93
+ global $wplnst_http_response;
94
+ $wplnst_http_response = '';
95
+
96
+ // Prepare CURL options
97
+ $curlopts = array(
98
+ 'CURLOPT_URL' => $url,
99
+ 'CURLOPT_HEADER' => true,
100
+ 'CURLINFO_HEADER_OUT' => true,
101
+ 'CURLOPT_NOBODY' => false,
102
+ 'CURLOPT_HTTPHEADER' => array('Expect:'),
103
+ 'CURLOPT_FOLLOWLOCATION' => false,
104
+ 'CURLOPT_RETURNTRANSFER' => false,
105
+ 'CURLOPT_FRESH_CONNECT' => true,
106
+ 'CURLOPT_CONNECTTIMEOUT' => $connect_timeout,
107
+ 'CURLOPT_TIMEOUT' => $request_timeout,
108
+ 'CURLOPT_USERAGENT' => $user_agent,
109
+ 'CURLOPT_WRITEFUNCTION' => 'wplnst_http_read_stream',
110
+ );
111
+
112
+ // IP resolve options
113
+ if (defined('CURL_IPRESOLVE_V4'))
114
+ $curlopts['CURLOPT_IPRESOLVE'] = CURL_IPRESOLVE_V4;
115
+
116
+ // HTTPS checks
117
+ if (0 === strpos($url, 'https')) {
118
+ $curlopts['CURLOPT_SSL_VERIFYHOST'] = false;
119
+ $curlopts['CURLOPT_SSL_VERIFYPEER'] = false;
120
+ }
121
+
122
+ // Do the request
123
+ $response = WPLNST_Core_CURL::request($curlopts, array('CURLINFO_HEADER_OUT', 'CURLINFO_TOTAL_TIME', 'CURLINFO_SIZE_DOWNLOAD', 'CURLINFO_HEADER_SIZE'));
124
+
125
+ // Check request headers
126
+ $headers_request = isset($response['info']['CURLINFO_HEADER_OUT'])? $response['info']['CURLINFO_HEADER_OUT'] : '';
127
+
128
+ // Check total time
129
+ $total_time = isset($response['info']['CURLINFO_TOTAL_TIME'])? (int) $response['info']['CURLINFO_TOTAL_TIME'] : false;
130
+ if (empty($total_time))
131
+ $total_time = $response['time'];
132
+
133
+ // Total size
134
+ $total_bytes = isset($response['info']['CURLINFO_SIZE_DOWNLOAD'])? (int) $response['info']['CURLINFO_SIZE_DOWNLOAD'] : false;
135
+ if (empty($total_bytes))
136
+ $total_bytes = strlen($wplnst_http_response);
137
+
138
+ // Headers size
139
+ $headers_size = isset($response['info']['CURLINFO_HEADER_SIZE'])? (int) $response['info']['CURLINFO_HEADER_SIZE'] : false;
140
+ if (empty($headers_size)) {
141
+ $headers_pos = strpos($wplnst_http_response, "\r\n\r\n");
142
+ $headers_size = (false === $headers_pos)? $total_bytes : $headers_pos + 4;
143
+ }
144
+
145
+ // Extract headers (trim to avoid conflicts with this script in API mode)
146
+ $wplnst_http_headers = trim(substr($wplnst_http_response, 0, $headers_size));
147
+
148
+ // Check errno exception when aborted with this plugin
149
+ if ('23' == $response['errno'] && (!empty($max_download_done) || !empty($only_headers_done)))
150
+ $response['errno'] = 0;
151
+
152
+
153
+ /* Back to WP */
154
+
155
+ // Populate POST fields
156
+ $postfields = array(
157
+ 'url_id' => $url_id,
158
+ 'headers' => $wplnst_http_headers,
159
+ 'headers_request' => $headers_request,
160
+ 'total_time' => $total_time,
161
+ 'total_bytes' => $total_bytes,
162
+ 'headers_size' => $headers_size,
163
+ 'curl_errno' => $response['errno'],
164
+ 'timestamp' => $response['timestamp'],
165
+ );
166
+
167
+ // Aditional body field
168
+ if (!empty($_POST['return_body']) && '1' == $_POST['return_body'])
169
+ $postfields['body'] = substr($wplnst_http_response, $headers_size);
170
+
171
+ // Check back URL
172
+ if (!empty($back_url)) {
173
+
174
+ // Debug point
175
+ self::debug('script back start');
176
+
177
+ // Spawn back URL call
178
+ WPLNST_Core_CURL::spawn(array(
179
+ 'CURLOPT_URL' => $back_url,
180
+ 'CURLOPT_USERAGENT' => 'WPLNST HTTP Requests script',
181
+ 'CURLOPT_POST' => true,
182
+ 'CURLOPT_POSTFIELDS' => http_build_query($postfields, null, '&'),
183
+ 'CURLOPT_HTTPHEADER' => array('Content-Type: application/x-www-form-urlencoded; charset=utf-8'),
184
+ ));
185
+
186
+ // Debug point
187
+ self::debug('script back end');
188
+
189
+ // End
190
+ self::terminate();
191
+ }
192
+
193
+ // End with post data
194
+ self::terminate($postfields);
195
+ }
196
+
197
+
198
+
199
+ // Response
200
+ // ---------------------------------------------------------------------------------------------------
201
+
202
+
203
+
204
+ /**
205
+ * Ends execution
206
+ */
207
+ private static function terminate($output = array()) {
208
+
209
+ // Initialize
210
+ $dump = '';
211
+
212
+ // Check array output
213
+ if (!empty($output) && is_array($output)) {
214
+
215
+ // JSON response
216
+ @header('Content-Type: application/json');
217
+
218
+ // Dump data
219
+ $dump = self::json_encode(array(
220
+ 'status' => 'ok',
221
+ 'data' => $output,
222
+ ));
223
+ }
224
+
225
+ // No cache headers
226
+ @header('Expires: Tue, 03 Jul 2001 06:00:00 GMT');
227
+ @header('Last-Modified: '.gmdate('D, d M Y H:i:s').' GMT');
228
+ @header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
229
+ @header('Cache-Control: post-check=0, pre-check=0', false);
230
+ @header('Pragma: no-cache');
231
+
232
+ // Avoid indexation
233
+ @header('X-Robots-Tag: noindex');
234
+
235
+ // Debug point
236
+ self::debug('script terminate');
237
+
238
+ // End
239
+ die($dump);
240
+ }
241
+
242
+
243
+
244
+ /**
245
+ * A driver or wrapper for JSON encode
246
+ */
247
+ private static function json_encode($string) {
248
+
249
+ // Check native function
250
+ if (function_exists('json_encode')) {
251
+ return @json_encode($string);
252
+
253
+ // Other
254
+ } else {
255
+
256
+ // Globals
257
+ global $wp_json;
258
+
259
+ // Check current instance
260
+ if (empty($wp_json) || !($wp_json instanceof Services_JSON)) {
261
+
262
+ // Include file
263
+ @require_once(dirname(__FILE__).'/class-json.php');
264
+
265
+ // Create new object
266
+ if (class_exists('Services_JSON'))
267
+ $wp_json = new Services_JSON();
268
+ }
269
+
270
+ // Check object
271
+ return (!empty($wp_json) && $wp_json instanceof Services_JSON)? $wp_json->encodeUnsafe($string) : false;
272
+ }
273
+ }
274
+
275
+
276
+
277
+ // Debug
278
+ // ---------------------------------------------------------------------------------------------------
279
+
280
+
281
+
282
+ /**
283
+ * Debug output
284
+ */
285
+ private static function debug($message) {
286
+
287
+ // Initialize
288
+ static $debug_info;
289
+ if (!isset($debug_info))
290
+ $debug_info = self::debug_info();
291
+
292
+ // Check debug
293
+ if (false === $debug_info)
294
+ return;
295
+
296
+ // Prefix
297
+ $prefix = '';
298
+ if (!empty($debug_info['scan_id'])) {
299
+ $prefix = 'scan '.$debug_info['scan_id'];
300
+ if (!empty($debug_info['thread_id']))
301
+ $prefix .= ' - thread '.$debug_info['thread_id'];
302
+ $prefix .= ' ';
303
+ }
304
+
305
+ // Output
306
+ error_log('WPLNST - '.$prefix.'[http] '.$message);
307
+ }
308
+
309
+
310
+
311
+ /**
312
+ * Debug info check
313
+ */
314
+ private static function debug_info() {
315
+
316
+ // Check debug argument
317
+ if (empty($_POST['debug']) || '1' != $_POST['debug'])
318
+ return false;
319
+
320
+ // Initialize
321
+ $debug_info = array();
322
+
323
+ // Check back URL
324
+ if (!empty($_POST['back_url'])) {
325
+
326
+ // Analyze back URL
327
+ $info = explode('&wplnst_', str_replace('?wplnst_', '&wplnst_', $_POST['back_url']));
328
+
329
+ // Enum elements
330
+ foreach ($info as $pair) {
331
+
332
+ // Check a pair
333
+ $pair = explode('=', $pair);
334
+ if (2 == count($pair)) {
335
+
336
+ // Scan
337
+ if ('crawl' == $pair[0]) {
338
+ $scan_id = (int) $pair[1];
339
+ if ($scan_id > 0)
340
+ $debug_info['scan_id'] = $scan_id;
341
+
342
+ // Thread
343
+ } elseif ('thread' == $pair[0]) {
344
+ $thread_id = (int) $pair[1];
345
+ if ($thread_id > 0)
346
+ $debug_info['thread_id'] = $thread_id;
347
+ }
348
+ }
349
+ }
350
+ }
351
+
352
+ // Done
353
+ return $debug_info;
354
+ }
355
+
356
+
357
+
358
+ }
359
+
360
+
361
+
362
+ // Callback functions
363
+ // ---------------------------------------------------------------------------------------------------
364
+
365
+
366
+
367
+ /**
368
+ * Response callback function
369
+ */
370
+ function wplnst_http_read_stream($ch, $line) {
371
+
372
+ // Globals
373
+ global $wplnst_http_response, $max_download, $max_download_done, $only_headers, $only_headers_done;
374
+
375
+ // Current lengths
376
+ $line_length = strlen($line);
377
+ $total_length = strlen($wplnst_http_response);
378
+
379
+ // Check overflow
380
+ if (($total_length + $line_length) > $max_download) {
381
+
382
+ // Check available size
383
+ $available = $max_download - $total_length;
384
+ if ($available > 0)
385
+ $wplnst_http_response .= substr($line, 0, $available);
386
+
387
+ // Max download achieved
388
+ $max_download_done = true;
389
+
390
+ // End
391
+ return 0;
392
+ }
393
+
394
+ // Add new chunk
395
+ $wplnst_http_response .= $line;
396
+
397
+ // Check only headers
398
+ if ($only_headers && false !== strpos($wplnst_http_response, "\r\n\r\n")) {
399
+
400
+ // Headers achieved
401
+ $only_headers_done = true;
402
+
403
+ // End
404
+ return 0;
405
+ }
406
+
407
+ // Done
408
+ return strlen($line);
409
+ }
410
+
411
+
412
+ // Run
413
+ WPLNST_Core_HTTP_Request::start();
414
+
415
+
416
+ // End check
417
+ endif;
core/scans.php ADDED
@@ -0,0 +1,1918 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * WP Link Status Core Scans class
5
+ *
6
+ * @package WP Link Status
7
+ * @subpackage WP Link Status Core
8
+ */
9
+ class WPLNST_Core_Scans {
10
+
11
+
12
+
13
+ // Retrieving scans data
14
+ // ---------------------------------------------------------------------------------------------------
15
+
16
+
17
+
18
+ /**
19
+ * Retrieve existing scans
20
+ */
21
+ public function get_scans($args) {
22
+
23
+ // Globals
24
+ global $wpdb;
25
+
26
+ // Prepare args
27
+ $args = array_merge(array(
28
+ 'setup_rows' => true,
29
+ 'setup_names' => false,
30
+ 'no_cache' => false,
31
+ ), $args);
32
+
33
+ // Set vars
34
+ extract($args);
35
+
36
+ // Params
37
+ $where = ' WHERE 1 = 1';
38
+ $limit_rows = false;
39
+
40
+ // Check scan id
41
+ if (!empty($scan_id))
42
+ $where .= ' AND scan_id = '.((int) $scan_id);
43
+
44
+ // Check limit
45
+ if (isset($limit)) {
46
+ $check = array_map('intval', array_map('trim', explode(',', $limit)));
47
+ if (1 == count($check)) {
48
+ if ($check[0] > 0)
49
+ $limit_rows = $check[0];
50
+ } elseif (2 == count($check)) {
51
+ if ($check[0] > 0 || $check[1] > 0)
52
+ $limit_rows = $check[0].', '.$check[1];
53
+ }
54
+ }
55
+
56
+ // Check single row
57
+ if (1 === $limit_rows) {
58
+
59
+ // Perform query
60
+ $sql = 'SELECT'.(empty($no_cache)? '' : ' SQL_NO_CACHE').' * FROM '.$wpdb->prefix.'wplnst_scans'.$where.(empty($order_by)? '' : ' ORDER BY '.$order_by).' LIMIT 1';
61
+ $results = (object) array('rows' => $wpdb->get_results($sql));
62
+
63
+ // Multiple rows
64
+ } else {
65
+
66
+ // Check page
67
+ $paged = empty($paged)? 1 : (int) $paged;
68
+ if (empty($paged))
69
+ $paged = 1;
70
+
71
+ // Check elements per page
72
+ $per_page = isset($per_page)? (int) $per_page : (int) get_user_option('wplnst_scans_per_page');
73
+ if (empty($per_page))
74
+ $per_page = WPLNST_Core_Types::scans_per_page;
75
+
76
+ // Execute query
77
+ $results = $this->pagination(array(
78
+ 'paged' => $paged,
79
+ 'per_page' => $per_page,
80
+ 'sql' => 'SELECT $$$fields$$$ FROM '.$wpdb->prefix.'wplnst_scans'.$where,
81
+ 'fields' => '*',
82
+ 'order_by' => (empty($order_by)? '' : $order_by),
83
+ 'calc_rows' => wplnst_get_bsetting('mysql_calc_rows'),
84
+ 'no_cache' => false,
85
+ ));
86
+ }
87
+
88
+ // Check setup
89
+ if (!$setup_rows || empty($results->rows))
90
+ return $results;
91
+
92
+ // Prepare rows
93
+ $rows = array();
94
+ foreach ($results->rows as $row)
95
+ $rows[] = $this->setup_row_scan($row, $setup_names);
96
+ $results->rows = $rows;
97
+
98
+ // Done
99
+ return $results;
100
+ }
101
+
102
+
103
+
104
+ /**
105
+ * Return scan by its id
106
+ */
107
+ public function get_scan_by_id($scan_id, $setup_names = false, $no_cache = false) {
108
+
109
+ // Retrieve scans list
110
+ $scans = $this->get_scans(array('scan_id' => $scan_id, 'setup_rows' => true, 'setup_names' => $setup_names, 'no_cache' => $no_cache, 'limit' => 1));
111
+
112
+ // Count elements, return first or false
113
+ return (!empty($scans->rows) && is_array($scans->rows) && 1 == count($scans->rows))? $scans->rows[0] : false;
114
+ }
115
+
116
+
117
+
118
+ /**
119
+ * Setup a scan database row
120
+ */
121
+ public function setup_row_scan($row, $names = false) {
122
+
123
+ // Cache post types
124
+ static $post_types;
125
+ if (!isset($post_types))
126
+ $post_types = WPLNST_Core_Types::get_post_types();
127
+
128
+ // Cache post types keys
129
+ static $post_types_keys;
130
+ if (!isset($post_types_keys))
131
+ $post_types_keys = array_keys($post_types);
132
+
133
+ // Cache post status
134
+ static $post_status_keys;
135
+ if (!isset($post_status_keys))
136
+ $post_status_keys = array_keys(WPLNST_Core_Types::get_post_status());
137
+
138
+ // Cache status level
139
+ static $status_levels;
140
+ if (!isset($status_levels))
141
+ $status_levels = WPLNST_Core_Types::get_status_levels();
142
+
143
+ // Cache status levels keys
144
+ static $status_levels_keys;
145
+ if (!isset($status_levels_keys))
146
+ $status_levels_keys = array_keys($status_levels);
147
+
148
+ // Cache status codes
149
+ static $status_codes_raw;
150
+ if (!isset($status_codes_raw))
151
+ $status_codes_raw = WPLNST_Core_Types::get_status_codes_raw();
152
+
153
+ // Cache status codes keys
154
+ static $status_codes_keys;
155
+ if (!isset($status_codes_keys))
156
+ $status_codes_keys = array_keys($status_codes_raw);
157
+
158
+ // Scan object
159
+ $scan = new stdClass;
160
+ $scan->row = $row;
161
+
162
+
163
+ /* scan values */
164
+
165
+ // Cast fields
166
+ $scan->id = (int) $row->scan_id;
167
+ $scan->name = $row->name;
168
+ $scan->status = $row->status;
169
+ $scan->ready = (1 == (int) $row->ready);
170
+ $scan->hash = $row->hash;
171
+
172
+ // Decode config json field
173
+ $config = @json_decode($row->config, true);
174
+
175
+ // General tab
176
+ $scan->destination_type = WPLNST_Core_Types::check_array_value($config, 'destination_type', array_keys(WPLNST_Core_Types::get_destination_types()), 'all');
177
+ $scan->time_scope = WPLNST_Core_Types::check_array_value($config, 'time_scope', array_keys(WPLNST_Core_Types::get_time_scopes()), 'anytime');
178
+ $scan->link_types = WPLNST_Core_Types::check_array_value($config, 'link_types', array_keys(WPLNST_Core_Types::get_link_types()), array());
179
+ $scan->crawl_order = WPLNST_Core_Types::check_array_value($config, 'crawl_order', array_keys(WPLNST_Core_Types::get_crawl_order()), 'desc');
180
+ $scan->redir_status = WPLNST_Core_Types::check_array_value($config, 'redir_status', true);
181
+ $scan->malformed = WPLNST_Core_Types::check_array_value($config, 'malformed', true);
182
+ $scan->notify_default = WPLNST_Core_Types::check_array_value($config, 'notify_default', true);
183
+ $scan->notify_address = WPLNST_Core_Types::check_array_value($config, 'notify_address', true);
184
+ $scan->notify_address_email = WPLNST_Core_Types::get_array_value($config, 'notify_address_email', '');
185
+
186
+ // Content options tab
187
+ $scan->post_types = WPLNST_Core_Types::check_array_value($config, 'post_types', $post_types_keys, array());
188
+ $scan->post_status = WPLNST_Core_Types::check_array_value($config, 'post_status', $post_status_keys, array());
189
+ $scan->check_posts = (!empty($scan->post_types) && is_array($scan->post_types) && !empty($scan->post_status) && is_array($scan->post_status));
190
+ $scan->comment_types = WPLNST_Core_Types::check_array_value($config, 'comment_types', array_keys(WPLNST_Core_Types::get_comment_types()), array());
191
+ $scan->check_comments = (!empty($scan->comment_types) && is_array($scan->comment_types));
192
+ $scan->check_blogroll = WPLNST_Core_Types::check_array_value($config, 'blogroll', true);
193
+
194
+ // Links status tab
195
+ $scan->status_levels = WPLNST_Core_Types::check_array_value($config, 'status_levels', $status_levels_keys, array());
196
+ $scan->status_codes = WPLNST_Core_Types::check_array_value($config, 'status_codes', array_keys(WPLNST_Core_Types::get_status_codes_raw()), array());
197
+
198
+ // Filters
199
+ $scan->custom_fields = WPLNST_Core_Types::check_array_json($config, 'custom_fields');
200
+ $scan->anchor_filters = WPLNST_Core_Types::check_array_json($config, 'anchor_filters');
201
+ $scan->include_urls = WPLNST_Core_Types::check_array_json($config, 'include_urls');
202
+ $scan->exclude_urls = WPLNST_Core_Types::check_array_json($config, 'exclude_urls');
203
+ $scan->html_attributes = WPLNST_Core_Types::check_array_json($config, 'html_attributes');
204
+ $scan->filtered_query = WPLNST_Core_Types::check_array_value($config, 'filtered_query', true);
205
+
206
+
207
+ /* scan config values names */
208
+
209
+ if ($names) {
210
+
211
+ // Destination and Time scope
212
+ $scan->destination_type_name = WPLNST_Core_Types::get_destination_type_name($scan->destination_type, 'all');
213
+ $scan->time_scope_name = WPLNST_Core_Types::get_time_scope_name($scan->time_scope, 'anytime');
214
+
215
+ // Links types
216
+ $scan->link_types_names = WPLNST_Core_Types::get_link_types_names($scan->link_types);
217
+
218
+ // Crawl order
219
+ $scan->crawl_order_name = WPLNST_Core_Types::get_crawl_order_name($scan->crawl_order, 'desc');
220
+
221
+ // Post types and status
222
+ $scan->post_types_names = WPLNST_Core_Types::get_field_values_names($post_types, $scan->post_types);
223
+ $scan->post_status_names = empty($scan->post_status)? array() : array_map('ucfirst', $scan->post_status);
224
+
225
+ // A strict mode of post types names
226
+ $scan->post_types_names_strict = array();
227
+ foreach ($scan->post_types as $post_type_value) {
228
+ if (isset($post_types[$post_type_value]))
229
+ $scan->post_types_names_strict[] = esc_html($post_types[$post_type_value]).' (<code>'.esc_html($post_type_value).'</code>)';
230
+ }
231
+
232
+ // Comment types
233
+ if ($scan->check_comments) {
234
+ $scan->comment_types_names = WPLNST_Core_Types::get_comment_types_names($scan->comment_types);
235
+ if (1 == count($scan->comment_types_names)) {
236
+ $scan->post_types_names[] = sprintf(__('%s comments', 'wplnst'), $scan->comment_types_names[0]);
237
+ } elseif (2 == count($scan->comment_types_names)) {
238
+ $scan->post_types_names[] = sprintf(__('%s and %s comments', 'wplnst'), $scan->comment_types_names[0], lcfirst($scan->comment_types_names[1]));
239
+ }
240
+ }
241
+
242
+ // Check blogroll type
243
+ if ($scan->check_blogroll)
244
+ $scan->post_types_names[] = __('Blogroll', 'wplnst');
245
+
246
+ // Links status combined
247
+ $scan->links_status_names = WPLNST_Core_Types::get_links_status_names_combined($scan->status_levels, $scan->status_codes);
248
+
249
+ // Links status levels
250
+ $scan->status_levels_names = array();
251
+ foreach ($scan->status_levels as $status_level) {
252
+ if (isset($status_levels[$status_level]))
253
+ $scan->status_levels_names[] = $status_level.'00s ' .$status_levels[$status_level];
254
+ }
255
+
256
+ // Links status codes
257
+ $scan->status_codes_names = array();
258
+ foreach ($scan->status_codes as $status_code) {
259
+ if (isset($status_codes_raw[$status_code]))
260
+ $scan->status_codes_names[] = $status_code.' ' .$status_codes_raw[$status_code];
261
+ }
262
+ }
263
+
264
+
265
+ /* Prepare trace values */
266
+
267
+ // Decode trace json field
268
+ $trace = @json_decode($row->trace, true);
269
+ if (empty($trace) || !is_array($trace))
270
+ $trace = array();
271
+ $scan->trace = $trace;
272
+
273
+
274
+ /* Prepare scan summary */
275
+
276
+ $summary = @json_decode($row->summary, true);
277
+ if (empty($summary) || !is_array($summary))
278
+ $summary = array();
279
+ $scan->summary = $summary;
280
+
281
+
282
+ /* Prepare threads values */
283
+
284
+ // Decode threads json field
285
+ $threads = @json_decode($row->threads, true);
286
+
287
+ // Create new object
288
+ $scan->threads = new stdClass;
289
+
290
+ // Assign object properties
291
+ $scan->threads->current = (empty($threads) || !is_array($threads))? array() : $threads;
292
+ $scan->threads->max = (int) $row->max_threads;
293
+ $scan->threads->connect_timeout = (int) $row->connect_timeout;
294
+ $scan->threads->request_timeout = (int) $row->request_timeout;
295
+
296
+ // Done
297
+ return $scan;
298
+ }
299
+
300
+
301
+
302
+ /**
303
+ * Remove existing scan data
304
+ */
305
+ public function delete_scan($scan_id) {
306
+
307
+ // Globals
308
+ global $wpdb;
309
+
310
+ // Remove from main scans table
311
+ $wpdb->query($wpdb->prepare('DELETE FROM '.$wpdb->prefix.'wplnst_scans WHERE scan_id = %d', $scan_id));
312
+
313
+ // Remove from status table
314
+ $wpdb->query($wpdb->prepare('DELETE FROM '.$wpdb->prefix.'wplnst_urls_status WHERE scan_id = %d', $scan_id));
315
+
316
+ // Remove from locations table
317
+ $wpdb->query($wpdb->prepare('DELETE FROM '.$wpdb->prefix.'wplnst_urls_locations WHERE scan_id = %d', $scan_id));
318
+
319
+ // Remove from locations attributes table
320
+ $wpdb->query($wpdb->prepare('DELETE FROM '.$wpdb->prefix.'wplnst_urls_locations_att WHERE scan_id = %d', $scan_id));
321
+
322
+ // Remove from objects table
323
+ $wpdb->query($wpdb->prepare('DELETE FROM '.$wpdb->prefix.'wplnst_scans_objects WHERE scan_id = %d', $scan_id));
324
+ }
325
+
326
+
327
+
328
+ /**
329
+ * Number of running scans
330
+ */
331
+ public function get_scans_play_count() {
332
+
333
+ // Globals
334
+ global $wpdb;
335
+
336
+ // Perform query
337
+ return (int) $wpdb->get_var('SELECT SQL_NO_CACHE COUNT(*) FROM '.$wpdb->prefix.'wplnst_scans WHERE status = "play"');
338
+ }
339
+
340
+
341
+
342
+ /**
343
+ * Return if is possible to play a new scan
344
+ */
345
+ public function can_play_more_scans() {
346
+ return ($this->get_scans_play_count() < wplnst_get_nsetting('max_scans'));
347
+ }
348
+
349
+
350
+
351
+ /**
352
+ * Check if is ready to start a crawl
353
+ */
354
+ public static function is_scan_ready($scan) {
355
+
356
+ // Initialize
357
+ $result = array();
358
+ $empty_post_types = false;
359
+
360
+ // Check link types
361
+ if (empty($scan->link_types) || !is_array($scan->link_types))
362
+ $result['link_types'] = __('There is not any <strong>link type</strong> selected, you need to select one or more.', 'wplnst');
363
+
364
+ // Check post types
365
+ if (empty($scan->post_types) || !is_array($scan->post_types))
366
+ $empty_post_types = true;
367
+
368
+ // Check post status
369
+ if (empty($scan->post_status) || !is_array($scan->post_status)) {
370
+ if (!$empty_post_types)
371
+ $result['post_status'] = __('Need to select any <strong>post status</strong> value for the selected post types.', 'wplnst');
372
+ }
373
+
374
+ // Check comments and blogroll
375
+ if (!$scan->check_comments && !$scan->check_blogroll) {
376
+ if ($empty_post_types) {
377
+ $result['post_types'] = __('There is not any kind of <strong>post type</strong>, <strong>comments</strong> or <strong>blogroll</strong> selected.', 'wplnst');
378
+ }
379
+ }
380
+
381
+ // Check status levels and status codes
382
+ if ((empty($scan->status_levels) || !is_array($scan->status_levels)) && (empty($scan->status_codes) || !is_array($scan->status_codes)))
383
+ $result['link_status'] = __('Missing selection of any <strong>links status</strong> level or status code.', 'wplnst');
384
+
385
+ // Done
386
+ return empty($result)? true : $result;
387
+ }
388
+
389
+
390
+
391
+ /*
392
+ * Update scan ready status
393
+ */
394
+ public function update_scan_ready($scan_id, $ready) {
395
+
396
+ // Globals
397
+ global $wpdb;
398
+
399
+ // Perform query
400
+ return $wpdb->update($wpdb->prefix.'wplnst_scans', array('ready' => $ready? 1 : 0), array('scan_id' => $scan_id));
401
+ }
402
+
403
+
404
+
405
+ /**
406
+ * Remove any stored stopped datetime
407
+ */
408
+ public function remove_stopped_time($scan_id) {
409
+ // Globals
410
+ global $wpdb;
411
+
412
+ // Perform query
413
+ return $wpdb->update($wpdb->prefix.'wplnst_scans', array('stopped_at' => '0000-00-00 00:00:00'), array('scan_id' => $scan_id));
414
+ }
415
+
416
+
417
+
418
+ /**
419
+ * Obtains fresh scan trace data
420
+ */
421
+ public function get_scan_trace($scan_id, $field = false) {
422
+
423
+ // Globals
424
+ global $wpdb;
425
+
426
+ // Retrieve fresh trace data
427
+ $trace = $wpdb->get_var($wpdb->prepare('SELECT SQL_NO_CACHE trace FROM '.$wpdb->prefix.'wplnst_scans WHERE scan_id = %d', $scan_id));
428
+ $trace = @json_decode($trace, true);
429
+ $trace = (empty($trace) || !is_array($trace))? array() : $trace;
430
+
431
+ // Check field or all values
432
+ return (false !== $field)? (isset($trace[$field])? $trace[$field] : false) : $trace;
433
+ }
434
+
435
+
436
+
437
+ /**
438
+ * Update scan trace values
439
+ */
440
+ public function update_scan_trace($scan_id, $values) {
441
+
442
+ // Globals
443
+ global $wpdb;
444
+
445
+ // Merge trace values
446
+ $trace = array_merge($this->get_scan_trace($scan_id), $values);
447
+
448
+ // Update trace values
449
+ return $wpdb->update($wpdb->prefix.'wplnst_scans', array('trace' => @json_encode($trace)), array('scan_id' => $scan_id));
450
+ }
451
+
452
+
453
+
454
+ /**
455
+ * Retrieve scan summary
456
+ */
457
+ public function get_scan_summary($scan_id) {
458
+
459
+ // Globals
460
+ global $wpdb;
461
+
462
+ // Retrieve fresh summary data
463
+ $summary = $wpdb->get_var($wpdb->prepare('SELECT SQL_NO_CACHE summary FROM '.$wpdb->prefix.'wplnst_scans WHERE scan_id = %d', $scan_id));
464
+ $summary = @json_decode($summary, true);
465
+
466
+ // Check data
467
+ return (empty($summary) || !is_array($summary))? array() : $summary;
468
+ }
469
+
470
+
471
+
472
+ /**
473
+ * Remove scan summary data by prefixed keys
474
+ */
475
+ public function remove_scan_summary_prefixed($scan_id, $prefixes) {
476
+
477
+ // Globals
478
+ global $wpdb;
479
+
480
+ // Check value
481
+ if (empty($prefixes))
482
+ return;
483
+
484
+ // Check array
485
+ if (!is_array($prefixes))
486
+ $prefixes = array($prefixes);
487
+
488
+ // Current data
489
+ $summary = $this->get_scan_summary($scan_id);
490
+ if (!empty($summary)) {
491
+
492
+ // Initialize
493
+ $summary2 = array();
494
+
495
+ // Enum summary data
496
+ foreach ($summary as $key => $value) {
497
+
498
+ // Check prefixes
499
+ $match = false;
500
+ foreach ($prefixes as $prefix) {
501
+ if (0 === stripos($key, $prefix)) {
502
+ $match = true;
503
+ break;
504
+ }
505
+ }
506
+
507
+ // Removed
508
+ if ($match)
509
+ continue;
510
+
511
+ // Copy data
512
+ $summary2[$key] = $value;
513
+ }
514
+
515
+ // Check summary update
516
+ if (count($summary) != count($summary2))
517
+ $this->update_scan_summary($scan_id, $summary2, false);
518
+ }
519
+ }
520
+
521
+
522
+
523
+ /**
524
+ * Update scan summary data
525
+ */
526
+ public function update_scan_summary($scan_id, $values, $merge = true) {
527
+
528
+ // Globals
529
+ global $wpdb;
530
+
531
+ // Check previous summary
532
+ $summary = $this->get_scan_summary($scan_id);
533
+
534
+ // Merge data
535
+ $summary = (empty($summary) || !$merge)? $values : array_merge($summary, $values);
536
+
537
+ // Update data
538
+ return $wpdb->update($wpdb->prefix.'wplnst_scans', array('summary' => @json_encode($summary)), array('scan_id' => $scan_id));
539
+ }
540
+
541
+
542
+
543
+ /**
544
+ * Update URL info for summary
545
+ */
546
+ public function set_scan_summary_status_codes($scan_id, $status_levels, $status_codes, $final = false) {
547
+
548
+ // Globals
549
+ global $wpdb;
550
+
551
+ // Not the last
552
+ if (!$final) {
553
+
554
+ // Check timestamp
555
+ $timestamp = $this->get_scan_trace($scan_id, 'summary_status_codes');
556
+ if (false !== $timestamp && (time() - $timestamp) < wplnst_get_nsetting('summary_status'))
557
+ return;
558
+
559
+ // Update timestamp
560
+ $this->update_scan_trace($scan_id, array('summary_status_codes' => time()));
561
+ }
562
+
563
+ // Check levels
564
+ if (!empty($status_levels) && is_array($status_levels))
565
+ $where_levels = 's.status_level IN ("0", "'.implode('", "', array_map('esc_sql', $status_levels)).'")';
566
+
567
+ // Check codes
568
+ if (!empty($status_codes) && is_array($status_codes))
569
+ $where_codes = 's.status_code IN ("0", "'.implode('", "', array_map('esc_sql', $status_codes)).'")';
570
+
571
+ // Compose combined
572
+ if (isset($where_levels) && isset($where_codes)) {
573
+ $where = ' AND ('.$where_levels.' OR '.$where_codes.')';
574
+
575
+ // Only levels
576
+ } elseif (isset($where_levels)) {
577
+ $where = ' AND '.$where_levels;
578
+
579
+ // Only codes
580
+ } elseif (isset($where_codes)) {
581
+ $where = ' AND '.$where_codes;
582
+
583
+ // Error
584
+ } else {
585
+ return;
586
+ }
587
+
588
+ // Initialize
589
+ $total = 0;
590
+ $codes = array();
591
+ $levels = array();
592
+
593
+ // Retrieve totals of codes for not ignored locations
594
+ $results = $wpdb->get_results($wpdb->prepare('SELECT s.status_code, COUNT(*) total FROM '.$wpdb->prefix.'wplnst_urls_status s RIGHT JOIN '.$wpdb->prefix.'wplnst_urls_locations l ON s.url_id = l.url_id AND s.scan_id = l.scan_id WHERE s.scan_id = %d '.$where.' AND s.phase = "end" AND l.ignored = 0 GROUP BY status_code', $scan_id));
595
+
596
+ // Cast data to array
597
+ if (!empty($results) && is_array($results)) {
598
+
599
+ // Enum code results
600
+ foreach ($results as $result) {
601
+
602
+ // Sum all codes
603
+ $total += (int) $result->total;
604
+
605
+ // Total for this code
606
+ $codes[$result->status_code] = (int) $result->total;
607
+
608
+ // Total for this level
609
+ $level_key = mb_substr($result->status_code, 0, 1);
610
+ $levels[$level_key] = isset($levels[$level_key])? $levels[$level_key] + $codes[$result->status_code] : $codes[$result->status_code];
611
+ }
612
+ }
613
+
614
+ // Prepare to summary
615
+ $summary_keys = array();
616
+
617
+ // Prefix code keys
618
+ foreach ($codes as $key => $value)
619
+ $summary_keys['status_code_'.$key] = $value;
620
+
621
+ // Prefix level keys
622
+ foreach ($levels as $key => $value)
623
+ $summary_keys['status_level_'.$key] = $value;
624
+
625
+ // Total results
626
+ $summary_keys['status_total'] = $total;
627
+
628
+ // Remove old summary status values
629
+ $this->remove_scan_summary_prefixed($scan_id, array('status_code_', 'status_level_'));
630
+
631
+ // Update summary data
632
+ $this->update_scan_summary($scan_id, $summary_keys);
633
+ }
634
+
635
+
636
+
637
+ /**
638
+ * Update URL info for summary
639
+ */
640
+ public function set_scan_summary_urls_phases($scan_id, $final = false) {
641
+
642
+ // Globals
643
+ global $wpdb;
644
+
645
+ // Not the last
646
+ if (!$final) {
647
+
648
+ // Check timestamp
649
+ $timestamp = $this->get_scan_trace($scan_id, 'summary_url_phases');
650
+ if (false !== $timestamp && (time() - $timestamp) < wplnst_get_nsetting('summary_phases'))
651
+ return;
652
+
653
+ // Update timestamp
654
+ $this->update_scan_trace($scan_id, array('summary_url_phases' => time()));
655
+ }
656
+
657
+ // Initialize
658
+ $phases = array();
659
+
660
+ // Retrieve totals of phases
661
+ $results = $wpdb->get_results($wpdb->prepare('SELECT phase, COUNT(*) total FROM '.$wpdb->prefix.'wplnst_urls_status WHERE scan_id = %d GROUP BY phase', $scan_id));
662
+
663
+ // Cast data to array
664
+ if (!empty($results) && is_array($results)) {
665
+ foreach ($results as $result)
666
+ $phases[$result->phase] = (int) $result->total;
667
+ }
668
+
669
+ // Merge default values
670
+ $phases = array_merge(array(
671
+ 'wait' => 0,
672
+ 'redir' => 0,
673
+ 'play' => 0,
674
+ 'end' => 0,
675
+ 'discard' => 0,
676
+ 'failed' => 0
677
+ ), $phases);
678
+
679
+ // Sum redir to wait
680
+ $phases['wait'] += $phases['redir'];
681
+
682
+ // New values combined
683
+ $phases['processed'] = $phases['end'] + $phases['discard'] + $phases['failed'];
684
+
685
+ // Prefix keys
686
+ $summary_phases = array();
687
+ foreach ($phases as $key => $value)
688
+ $summary_phases['urls_phase_'.$key] = $value;
689
+
690
+ // Update summary data
691
+ $this->update_scan_summary($scan_id, $summary_phases);
692
+ }
693
+
694
+
695
+
696
+ /**
697
+ * Obtains the total match objects and update summary
698
+ */
699
+ public function set_scan_summary_objects_match($scan_id, $object_type, $final = false) {
700
+
701
+ // Globals
702
+ global $wpdb;
703
+
704
+ // Not the last
705
+ if (!$final) {
706
+
707
+ // Check timestamp
708
+ $timestamp = $this->get_scan_trace($scan_id, 'summary_objects_match');
709
+ if (false !== $timestamp && (time() - $timestamp) < wplnst_get_nsetting('summary_objects'))
710
+ return;
711
+
712
+ // Update timestamp
713
+ $this->update_scan_trace($scan_id, array('summary_objects_match' => time()));
714
+ }
715
+
716
+ // Perform query
717
+ $objects_match = (int) $wpdb->get_var($wpdb->prepare('SELECT COUNT(DISTINCT object_id) FROM '.$wpdb->prefix.'wplnst_urls_locations WHERE scan_id = %d AND object_type = %s', $scan_id, $object_type));
718
+
719
+ // Update summary data
720
+ $this->update_scan_summary($scan_id, array('objects_match_'.$object_type => $objects_match));
721
+ }
722
+
723
+
724
+
725
+ /**
726
+ * Save max threads and timeouts connection options
727
+ * This works at the end of an scan crawling process
728
+ */
729
+ public function set_scan_final_threads_options($scan_id, $args) {
730
+
731
+ // Globals
732
+ global $wpdb;
733
+
734
+ $args = array_merge(array(
735
+ 'max_threads' => 0,
736
+ 'connect_timeout' => 0,
737
+ 'request_timeout' => 0,
738
+ ), $args);
739
+
740
+ // Prepare update
741
+ $update = array(
742
+ 'max_threads' => wplnst_get_nsetting('max_threads', $args['max_threads']),
743
+ 'connect_timeout' => wplnst_get_nsetting('connect_timeout', $args['connect_timeout']),
744
+ 'request_timeout' => wplnst_get_nsetting('request_timeout', $args['request_timeout']),
745
+ );
746
+
747
+ // Update
748
+ return $wpdb->update($wpdb->prefix.'wplnst_scans', $update, array('scan_id' => $scan_id));
749
+ }
750
+
751
+
752
+
753
+ // Scan status queries
754
+ // ---------------------------------------------------------------------------------------------------
755
+
756
+
757
+
758
+ /**
759
+ * Obtains fresh scan status data
760
+ */
761
+ public function get_scan_status($scan_id) {
762
+
763
+ // Globals
764
+ global $wpdb;
765
+
766
+ // Return status
767
+ return $wpdb->get_var($wpdb->prepare('SELECT SQL_NO_CACHE status FROM '.$wpdb->prefix.'wplnst_scans WHERE scan_id = %d', $scan_id));
768
+ }
769
+
770
+
771
+
772
+ /**
773
+ * Stop any playing scan
774
+ */
775
+ public function stop_playing_scans() {
776
+
777
+ // Globals
778
+ global $wpdb;
779
+
780
+ // Perform query
781
+ return $wpdb->update($wpdb->prefix.'wplnst_scans', array('status' => 'stop'), array('status' => 'play'));
782
+ }
783
+
784
+
785
+
786
+ /**
787
+ * Queue a given scan
788
+ */
789
+ public function queue_scan($scan_id) {
790
+
791
+ // Globals
792
+ global $wpdb;
793
+
794
+ // Perform query
795
+ return $wpdb->update($wpdb->prefix.'wplnst_scans', array('status' => 'queued', 'enqueued_at' => current_time('mysql', true)), array('scan_id' => $scan_id));
796
+ }
797
+
798
+
799
+
800
+ /**
801
+ * Unqueue scan, cast to wait
802
+ */
803
+ public function unqueue_scan($scan_id) {
804
+
805
+ // Globals
806
+ global $wpdb;
807
+
808
+ // Perform query
809
+ return $wpdb->update($wpdb->prefix.'wplnst_scans', array('status' => 'wait'), array('scan_id' => $scan_id));
810
+ }
811
+
812
+
813
+
814
+ /**
815
+ * Play a given scan
816
+ */
817
+ public function play_scan($scan_id, $continued = false) {
818
+
819
+ // Globals
820
+ global $wpdb;
821
+
822
+ // Decide field
823
+ $time_field = $continued? 'continued_at' : 'started_at';
824
+
825
+ // Perform query
826
+ return $wpdb->update($wpdb->prefix.'wplnst_scans', array('status' => 'play', $time_field => current_time('mysql', true)), array('scan_id' => $scan_id));
827
+ }
828
+
829
+
830
+
831
+ /**
832
+ * Stop a given scan
833
+ */
834
+ public function stop_scan($scan_id) {
835
+
836
+ // Globals
837
+ global $wpdb;
838
+
839
+ // Perform query
840
+ return $wpdb->update($wpdb->prefix.'wplnst_scans', array('status' => 'stop', 'stopped_at' => current_time('mysql', true)), array('scan_id' => $scan_id));
841
+ }
842
+
843
+
844
+
845
+ /**
846
+ * Terminated scan
847
+ */
848
+ public function end_scan($scan_id) {
849
+
850
+ // Globals
851
+ global $wpdb;
852
+
853
+ // Perform query
854
+ return $wpdb->update($wpdb->prefix.'wplnst_scans', array('status' => 'end', 'finished_at' => current_time('mysql', true)), array('scan_id' => $scan_id));
855
+ }
856
+
857
+
858
+
859
+ // URLs crawling procedures
860
+ // ---------------------------------------------------------------------------------------------------
861
+
862
+
863
+
864
+ /**
865
+ * Replacement function of WP API because we need meta_id
866
+ */
867
+ public function get_post_metas($post_id) {
868
+
869
+ // Globals
870
+ global $wpdb;
871
+
872
+ // Check keys input
873
+ if (!empty($keys) && !is_array($keys))
874
+ $keys = array($keys);
875
+
876
+ // Retrieve metas
877
+ $rows = $wpdb->get_results($wpdb->prepare('SELECT * FROM '.$wpdb->postmeta.' WHERE post_id = %d ORDER BY meta_id ASC', $post_id));
878
+
879
+ // Check results
880
+ if (!empty($rows) && is_array($rows)) {
881
+
882
+ // Initialize
883
+ $metas = array();
884
+
885
+ // Enum rrows
886
+ foreach ($rows as $row) {
887
+
888
+ // Check meta key
889
+ if (empty($row->meta_key))
890
+ continue;
891
+
892
+ // Check by name
893
+ if (!isset($metas[$row->meta_key]))
894
+ $metas[$row->meta_key] = array();
895
+
896
+ // Add value
897
+ $metas[$row->meta_key][$row->meta_id] = $row->meta_value;
898
+ }
899
+
900
+ // Done
901
+ return $metas;
902
+ }
903
+
904
+ // Not found
905
+ return false;
906
+ }
907
+
908
+
909
+
910
+ /**
911
+ * Register by identifiers before process the object
912
+ */
913
+ public function register_scan_object($scan_id, $object_id, $object_type, $object_date_gmt = "0000-00-00 00:00:00") {
914
+
915
+ // Globals
916
+ global $wpdb;
917
+
918
+ // Insert attempt
919
+ return (1 == (int) $wpdb->query($wpdb->prepare('INSERT IGNORE INTO '.$wpdb->prefix.'wplnst_scans_objects SET scan_id = %d, object_id = %d, object_type = %s, object_date_gmt = %s', $scan_id, $object_id, $object_type, $object_date_gmt)));
920
+ }
921
+
922
+
923
+
924
+ /**
925
+ * Check if exists an scan object
926
+ */
927
+ public function scan_object_exists($scan_id, $object_id, $object_type) {
928
+
929
+ // Globals
930
+ global $wpdb;
931
+
932
+ // Number of occurrences
933
+ return (1 == (int) $wpdb->get_var($wpdb->prepare('SELECT SQL_NO_CACHE COUNT(*) FROM '.$wpdb->prefix.'wplnst_scans_objects WHERE scan_id = %d AND object_id = %d AND object_type = %s', $scan_id, $object_id, $object_type)));
934
+ }
935
+
936
+
937
+
938
+ /**
939
+ * Retrieve object identifiers based on type and same date
940
+ */
941
+ public function get_scan_objects_ids_by_date($scan_id, $object_type, $object_date_gmt) {
942
+
943
+ // Globals
944
+ global $wpdb;
945
+
946
+ // Number of occurrences
947
+ return $wpdb->get_col($wpdb->prepare('SELECT SQL_NO_CACHE object_id FROM '.$wpdb->prefix.'wplnst_scans_objects WHERE scan_id = %d AND object_type = %s AND object_date_gmt = %s', $scan_id, $object_type, $object_date_gmt));
948
+ }
949
+
950
+
951
+
952
+ /**
953
+ * Return the amount of registered objects
954
+ */
955
+ public function get_scan_objects_count($scan_id, $object_type) {
956
+
957
+ // Globals
958
+ global $wpdb;
959
+
960
+ // Insert attempt
961
+ return (int) $wpdb->get_var($wpdb->prepare('SELECT COUNT(*) FROM '.$wpdb->prefix.'wplnst_scans_objects WHERE scan_id = %d AND object_type = %s', $scan_id, $object_type));
962
+ }
963
+
964
+
965
+
966
+ /**
967
+ * Remove all scan registered objects
968
+ */
969
+ public function remove_scan_objects($scan_id) {
970
+
971
+ // Globals
972
+ global $wpdb;
973
+
974
+ // Insert attempt
975
+ return (int) $wpdb->get_var($wpdb->prepare('DELETE FROM '.$wpdb->prefix.'wplnst_scans_objects WHERE scan_id = %d', $scan_id));
976
+ }
977
+
978
+
979
+
980
+ /**
981
+ * Retrieve scan URL data
982
+ */
983
+ public function get_scan_url($args) {
984
+
985
+ // Globals
986
+ global $wpdb;
987
+
988
+ // Arguments
989
+ extract($args);
990
+
991
+ // Check by id
992
+ if (isset($id)) {
993
+
994
+ // Check identifier
995
+ $id = (int) $id;
996
+ if (!empty($id)) {
997
+ $row = $wpdb->get_row($wpdb->prepare('SELECT '.(isset($no_cache)? 'SQL_NO_CACHE ' : '').'* FROM '.$wpdb->prefix.'wplnst_urls WHERE url_id = %d', $id));
998
+ return empty($row)? false : $row;
999
+ }
1000
+
1001
+ // By URL string
1002
+ } elseif (isset($url)) {
1003
+
1004
+ // Retrieve URLs by hash
1005
+ $rows = $wpdb->get_results($wpdb->prepare('SELECT '.(isset($no_cache)? 'SQL_NO_CACHE ' : '').'* FROM '.$wpdb->prefix.'wplnst_urls WHERE hash = %s ORDER BY url_id ASC', $this->get_url_hash($url)));
1006
+ if (empty($rows) || !is_array($rows) || 1 != count($rows))
1007
+ return false;
1008
+
1009
+ // Done
1010
+ return $rows[0];
1011
+ }
1012
+
1013
+ // Default
1014
+ return false;
1015
+ }
1016
+
1017
+
1018
+
1019
+ /**
1020
+ * Creates a 64 bytes hash to avoid URLs collisions
1021
+ */
1022
+ public function get_url_hash($url) {
1023
+
1024
+ // First part
1025
+ $hash = md5($url);
1026
+
1027
+ // Another version
1028
+ $url2 = array();
1029
+
1030
+ // Special chars
1031
+ static $special;
1032
+ if (!isset($special))
1033
+ $special = array(";", "/", "?", ":", "@", "&", "=", "+", "$", ",", "-", "_", ".", "!", "~", "*", "'", "(", ")");
1034
+
1035
+ // Enum URL chars
1036
+ $length = mb_strlen($url);
1037
+ for ($i = 0; $i < $length; $i++) {
1038
+ $char = mb_substr($url, $i, 1);
1039
+ if (!in_array($char, $special))
1040
+ $url2[] = $char;
1041
+ }
1042
+
1043
+ // Return combined hash
1044
+ return $hash.md5(implode('', array_reverse($url2)));
1045
+ }
1046
+
1047
+
1048
+
1049
+ /**
1050
+ * Retrieve next URL with next value
1051
+ */
1052
+ public function get_scan_url_waiting($scan_id) {
1053
+
1054
+ // Retrieve by wait status
1055
+ if (false !== ($rows = $this->get_scan_url_status(array(
1056
+ 'scan_id' => $scan_id,
1057
+ 'phase' => array('wait', 'redir'),
1058
+ 'order' => 'created_at ASC',
1059
+ 'no_cache' => true,
1060
+ 'limit_rows' => 1,
1061
+ )))) {
1062
+
1063
+ // Retrieve url
1064
+ $url = $this->get_scan_url(array('id' => $rows[0]->url_id, 'no_cache' => true));
1065
+ if (false !== $url) {
1066
+
1067
+ // Check a redirection
1068
+ if ('redir' == $rows[0]->phase) {
1069
+ $url->url = $rows[0]->redirect_url;
1070
+ $url->redirection = true;
1071
+ }
1072
+ }
1073
+
1074
+ // Done
1075
+ return $url;
1076
+ }
1077
+
1078
+ // Default
1079
+ return false;
1080
+ }
1081
+
1082
+
1083
+
1084
+ /**
1085
+ * Add new URL scan
1086
+ */
1087
+ public function add_scan_url($link, $scan_id) {
1088
+
1089
+ // Globals
1090
+ global $wpdb;
1091
+
1092
+ // Create hash
1093
+ $hash = $this->get_url_hash($link['url']);
1094
+
1095
+ // Prepare fields
1096
+ $scheme = isset($link['scheme'])? mb_substr($link['scheme'], 0, 20) : '';
1097
+ $host = isset($link['host'])? mb_substr($link['host'], 0, 255) : '';
1098
+ $path = isset($link['path'])? mb_substr($link['path'], 0, 255) : '';
1099
+ $query = isset($link['query'])? mb_substr($link['query'], 0, 255) : '';
1100
+ $scope = isset($link['scope'])? $link['scope'] : '';
1101
+
1102
+ // Add new scan status
1103
+ $result = $wpdb->query($wpdb->prepare('INSERT IGNORE INTO '.$wpdb->prefix.'wplnst_urls SET url = %s, hash = %s, scheme = %s, host = %s, path = %s, query = %s, scope = %s, created_at = %s, last_scan_id = %d', $link['url'], $hash, $scheme, $host, $path, $query, $scope, current_time('mysql', true), $scan_id));
1104
+ if (!empty($wpdb->insert_id)) {
1105
+ return $wpdb->insert_id;
1106
+
1107
+ // Collision?
1108
+ } else {
1109
+
1110
+ // Retrieve existing URL record
1111
+ $rows = $wpdb->get_results($wpdb->prepare('SELECT url_id, url FROM '.$wpdb->prefix.'wplnst_urls WHERE hash = %s', $hash));
1112
+ if (empty($rows) || !is_array($rows) || 1 != count($rows))
1113
+ return false;
1114
+
1115
+ // Return id only if URLs are the same
1116
+ return ($rows[0]->url === $url)? $rows[0]->url_id : false;
1117
+ }
1118
+ }
1119
+
1120
+
1121
+
1122
+ /**
1123
+ * Update URL scan data
1124
+ */
1125
+ public function update_scan_url($url_id, $update) {
1126
+
1127
+ // Globals
1128
+ global $wpdb;
1129
+
1130
+ // Update play value
1131
+ return $wpdb->update($wpdb->prefix.'wplnst_urls', $update, array('url_id' => $url_id));
1132
+ }
1133
+
1134
+
1135
+
1136
+ /**
1137
+ * Add location for scan url
1138
+ */
1139
+ public function add_scan_url_location($url_id, $scan_id, $link) {
1140
+
1141
+ // Globals
1142
+ global $wpdb;
1143
+
1144
+ // Add new url
1145
+ $wpdb->insert($wpdb->prefix.'wplnst_urls_locations', array(
1146
+ 'url_id' => $url_id,
1147
+ 'scan_id' => $scan_id,
1148
+ 'link_type' => $link['link_type'],
1149
+ 'object_id' => $link['object_id'],
1150
+ 'object_type' => $link['object_type'],
1151
+ 'object_post_type' => $link['object_post_type'],
1152
+ 'object_field' => $link['object_field'],
1153
+ 'object_date_gmt' => $link['object_date_gmt'],
1154
+ 'detected_at' => current_time('mysql', true),
1155
+ 'chunk' => isset($link['chunk'])? $link['chunk'] : '',
1156
+ 'anchor' => isset($link['anchor'])? $link['anchor'] : '',
1157
+ 'raw_url' => $link['raw'],
1158
+ 'fragment' => $link['fragment'],
1159
+ 'spaced' => $link['spaced']? 1 : 0,
1160
+ 'malformed' => $link['malformed']? 1 : 0,
1161
+ 'absolute' => $link['absolute']? 1 : 0,
1162
+ 'protorel' => $link['protorel']? 1 : 0,
1163
+ 'relative' => $link['relative']? 1 : 0,
1164
+ 'nofollow' => $link['nofollow']? 1 : 0,
1165
+ ));
1166
+
1167
+ // Result
1168
+ return empty($wpdb->insert_id)? false : $wpdb->insert_id;
1169
+ }
1170
+
1171
+
1172
+
1173
+ /**
1174
+ * Update location fields
1175
+ */
1176
+ public function update_scan_url_location($loc_id, $update) {
1177
+
1178
+ // Globals
1179
+ global $wpdb;
1180
+
1181
+ // Update
1182
+ return $wpdb->update($wpdb->prefix.'wplnst_urls_locations', $update, array('loc_id' => $loc_id));
1183
+ }
1184
+
1185
+
1186
+
1187
+ /**
1188
+ * Update several locations
1189
+ */
1190
+ public function update_scan_url_locations($loc_ids, $update) {
1191
+ foreach ($loc_ids as $loc_id)
1192
+ $this->update_scan_url_location($loc_id, $update);
1193
+ }
1194
+
1195
+
1196
+
1197
+ /**
1198
+ * Update posts fields
1199
+ */
1200
+ public function update_scan_post($post_id, $update, $clean_cache = true) {
1201
+
1202
+ // Globals
1203
+ global $wpdb;
1204
+
1205
+ // Update
1206
+ $result = $wpdb->update($wpdb->posts, $update, array('ID' => $post_id));
1207
+
1208
+ // Check cache
1209
+ if ($clean_cache)
1210
+ clean_post_cache($post_id);
1211
+
1212
+ // Done
1213
+ return $result;
1214
+ }
1215
+
1216
+
1217
+
1218
+ /**
1219
+ * Update meta associated to post
1220
+ */
1221
+ public function update_scan_post_meta($post_id, $meta_id, $content, $clean_cache = true) {
1222
+
1223
+ // Globals
1224
+ global $wpdb;
1225
+
1226
+ // Update
1227
+ $result = $wpdb->update($wpdb->postmeta, array('meta_value' => $content), array('meta_id' => $meta_id));
1228
+
1229
+ // Check cache
1230
+ if ($clean_cache)
1231
+ clean_post_cache($post_id);
1232
+
1233
+ // Done
1234
+ return $result;
1235
+ }
1236
+
1237
+
1238
+
1239
+ /**
1240
+ * Update comment fields
1241
+ */
1242
+ public function update_scan_comment($comment_id, $update, $clean_cache = true) {
1243
+
1244
+ // Globals
1245
+ global $wpdb;
1246
+
1247
+ // Update
1248
+ $result = $wpdb->update($wpdb->comments, $update, array('comment_ID' => $comment_id));
1249
+
1250
+ // Check cache
1251
+ if ($clean_cache)
1252
+ clean_comment_cache($comment_id);
1253
+
1254
+ // Done
1255
+ return $result;
1256
+ }
1257
+
1258
+
1259
+
1260
+ /**
1261
+ * Update bookmark fields
1262
+ */
1263
+ public function update_scan_bookmark($link_id, $update, $clean_cache = true) {
1264
+
1265
+ // Globals
1266
+ global $wpdb;
1267
+
1268
+ // Update
1269
+ $result = $wpdb->update($wpdb->links, $update, array('link_id' => $link_id));
1270
+
1271
+ // Check cache
1272
+ if ($clean_cache)
1273
+ clean_bookmark_cache($link_id);
1274
+
1275
+ // Done
1276
+ return $result;
1277
+ }
1278
+
1279
+
1280
+
1281
+ /**
1282
+ * Update total content for a given URL
1283
+ */
1284
+ public function set_scan_url_status_total_content($url_id, $scan_id) {
1285
+
1286
+ // Globals
1287
+ global $wpdb;
1288
+
1289
+ // Initialize
1290
+ $total = array();
1291
+
1292
+ // Retrieve total rows by object type
1293
+ $results = $wpdb->get_results($wpdb->prepare('SELECT object_type, COUNT(*) total FROM '.$wpdb->prefix.'wplnst_urls_locations WHERE url_id = %d AND scan_id = %d GROUP BY object_type', $url_id, $scan_id));
1294
+
1295
+ // Cast data to array
1296
+ if (!empty($results) && is_array($results)) {
1297
+ foreach ($results as $result)
1298
+ $total[$result->object_type] = (int) $result->total;
1299
+ }
1300
+
1301
+ // Merge default values
1302
+ $total = array_merge(array(
1303
+ 'posts' => 0,
1304
+ 'comments' => 0,
1305
+ 'blogroll' => 0,
1306
+ ), $total);
1307
+
1308
+ // Update scan URL status row
1309
+ $this->update_scan_url_status($url_id, $scan_id, array('total_posts' => $total['posts'], 'total_comments' => $total['comments'], 'total_blogroll' => $total['blogroll']));
1310
+ }
1311
+
1312
+
1313
+
1314
+ /**
1315
+ * Retrieve URL status info
1316
+ */
1317
+ public function get_scan_url_status($args = array()) {
1318
+
1319
+ // Globals
1320
+ global $wpdb;
1321
+
1322
+ // Check arguments
1323
+ if (empty($args) || !is_array($args))
1324
+ return false;
1325
+
1326
+ // Extract parameters
1327
+ extract($args);
1328
+
1329
+ // Cast arguments
1330
+ $url_id = isset($url_id)? (int) $url_id : 0;
1331
+ $scan_id = isset($scan_id)? (int) $scan_id : 0;
1332
+ $status = isset($status)? $status : false;
1333
+ $phase = isset($phase)? $phase : false;
1334
+
1335
+ // Check identifiers
1336
+ if (empty($url_id) && empty($scan_id))
1337
+ return false;
1338
+
1339
+ // Force cache for an isolated record
1340
+ if (!isset($no_cache) && !empty($url_id) && !empty($scan_id))
1341
+ $no_cache = true;
1342
+
1343
+ // Initialize
1344
+ $where = '1 = 1';
1345
+
1346
+ // Check URL id
1347
+ if (!empty($url_id))
1348
+ $where .= ' AND url_id = '.$url_id;
1349
+
1350
+ // Check scan id
1351
+ if (!empty($scan_id))
1352
+ $where .= ' AND scan_id = '.$scan_id;
1353
+
1354
+ // Check status
1355
+ if (false !== $status)
1356
+ $where .= ' AND status = "'.esc_sql($status).'"';
1357
+
1358
+ // Check phase
1359
+ if (false !== $phase)
1360
+ $where .= ' AND phase'.(is_array($phase)? ' IN ("'.implode('", "', array_map('esc_sql', $phase)).'")' : ' = "'.esc_sql($phase).'"');
1361
+
1362
+ // Order
1363
+ $order_by = '';
1364
+ if (isset($order)) {
1365
+ $order = explode(',', $order);
1366
+ foreach ($order as $order_item) {
1367
+ $order_item = explode(' ', $order_item);
1368
+ if (2 == count($order_item)) {
1369
+ if (in_array($order_item[0], array('url_id', 'scan_id', 'status', 'created_at', 'started_at'))) {
1370
+ $order_item[1] = strtoupper($order_item[1]);
1371
+ if ('ASC' == $order_item[1] || 'DESC' == $order_item[1])
1372
+ $order_by .= (empty($order_by)? ' ORDER BY ' : $order_by.', ').$order_item[0].' '.$order_item[1];
1373
+ }
1374
+ }
1375
+ }
1376
+ }
1377
+
1378
+ // Limit
1379
+ $limit = '';
1380
+ if (isset($limit_rows)) {
1381
+ $limit_rows = (int) $limit_rows;
1382
+ if (!empty($limit_rows))
1383
+ $limit = ' LIMIT '.(isset($limit_base)? (int) $limit_base.', ' : '').$limit_rows;
1384
+ }
1385
+
1386
+ // Perform query
1387
+ $rows = $wpdb->get_results('SELECT '.(isset($no_cache)? 'SQL_NO_CACHE ' : '').'* FROM '.$wpdb->prefix.'wplnst_urls_status WHERE '.$where.$order_by.$limit);
1388
+
1389
+ // Done
1390
+ return (empty($rows) || !is_array($rows))? false : $rows;
1391
+ }
1392
+
1393
+
1394
+
1395
+ /**
1396
+ * New URL status record
1397
+ */
1398
+ public function new_scan_url_status($url_id, $scan_id, $insert) {
1399
+
1400
+ // Globals
1401
+ global $wpdb;
1402
+
1403
+ // Add new scan status
1404
+ return $wpdb->query($wpdb->prepare('INSERT IGNORE INTO '.$wpdb->prefix.'wplnst_urls_status SET url_id = %d, scan_id = %d, phase = %s, created_at = %s', $url_id, $scan_id, $phase, current_time('mysql', true)));
1405
+ }
1406
+
1407
+
1408
+
1409
+ /**
1410
+ * Insert new URL status record
1411
+ */
1412
+ public function add_scan_url_status($url_id, $scan_id, $phase) {
1413
+
1414
+ // Globals
1415
+ global $wpdb;
1416
+
1417
+ // Add new scan status
1418
+ return $wpdb->query($wpdb->prepare('INSERT IGNORE INTO '.$wpdb->prefix.'wplnst_urls_status SET url_id = %d, scan_id = %d, phase = %s, created_at = %s', $url_id, $scan_id, $phase, current_time('mysql', true)));
1419
+ }
1420
+
1421
+
1422
+
1423
+ /**
1424
+ * Update a URL status phase
1425
+ */
1426
+ public function update_scan_url_status($url_id, $scan_id, $update) {
1427
+
1428
+ // Globals
1429
+ global $wpdb;
1430
+
1431
+ // Update play value
1432
+ return $wpdb->update($wpdb->prefix.'wplnst_urls_status', $update, array('url_id' => $url_id, 'scan_id' => $scan_id));
1433
+ }
1434
+
1435
+
1436
+
1437
+ /**
1438
+ * Remove URL status record
1439
+ */
1440
+ public function remove_scan_url_status($url_id, $scan_id) {
1441
+
1442
+ // Globals
1443
+ global $wpdb;
1444
+
1445
+ // Remove record
1446
+ return $wpdb->query($wpdb->prepare('DELETE FROM '.$wpdb->prefix.'wplnst_urls_status WHERE url_id = %d AND scan_id = %d LIMIT 1', $url_id, $scan_id));
1447
+ }
1448
+
1449
+
1450
+
1451
+ /**
1452
+ * Remove discard URL status and locations
1453
+ */
1454
+ public function remove_scan_discard_urls($scan_id) {
1455
+
1456
+ // Globals
1457
+ global $wpdb;
1458
+
1459
+ // Remove locations
1460
+ $wpdb->query($wpdb->prepare('DELETE FROM '.$wpdb->prefix.'wplnst_urls_locations WHERE scan_id = %d AND url_id IN (SELECT url_id FROM '.$wpdb->prefix.'wplnst_urls_status WHERE scan_id = %d AND phase = "discard")', $scan_id, $scan_id));
1461
+
1462
+ // Remove URL status
1463
+ $wpdb->query($wpdb->prepare('DELETE FROM '.$wpdb->prefix.'wplnst_urls_status WHERE scan_id = %d AND phase = "discard"', $scan_id));
1464
+ }
1465
+
1466
+
1467
+
1468
+ // Combined results from scans, urls, status and locations
1469
+ // ---------------------------------------------------------------------------------------------------
1470
+
1471
+
1472
+
1473
+ /**
1474
+ * Retrieve URL request headers
1475
+ */
1476
+ public function get_scan_result_headers($scan_id, $url_id) {
1477
+
1478
+ // Globals
1479
+ global $wpdb;
1480
+
1481
+ // Retrieve headers
1482
+ return $wpdb->get_row($wpdb->prepare('SELECT headers, headers_request, request_at, total_time, total_bytes FROM '.$wpdb->prefix.'wplnst_urls_status WHERE url_id = %d AND scan_id = %d', $url_id, $scan_id));
1483
+ }
1484
+
1485
+
1486
+
1487
+ /**
1488
+ * Retrieve scan results
1489
+ */
1490
+ public function get_scan_results($args) {
1491
+
1492
+ // Globals
1493
+ global $wpdb;
1494
+
1495
+ // Check arguments
1496
+ if (empty($args) || !is_array($args))
1497
+ return false;
1498
+
1499
+ // Extract parameters
1500
+ extract($args);
1501
+
1502
+ // Check scan id
1503
+ $scan_id = empty($scan_id)? 0 : (int) $scan_id;
1504
+ if (empty($scan_id))
1505
+ return false;
1506
+
1507
+ // Check page
1508
+ $paged = empty($paged)? 1 : (int) $paged;
1509
+ if (empty($paged))
1510
+ $paged = 1;
1511
+
1512
+ // Check elements per page
1513
+ $per_page = isset($per_page)? (int) $per_page : (int) get_user_option('wplnst_scan_results_per_page');
1514
+ if (empty($per_page))
1515
+ $per_page = WPLNST_Core_Types::scans_results_per_page;
1516
+
1517
+ // Prepare fields
1518
+ $fields = 'u.url_id, u.url, u.scheme, u.host, u.scope,';
1519
+ $fields .= 's.status_level, s.status_code, s.curl_errno, s.phase, s.redirect_url, s.redirect_steps, s.redirect_url_id, s.redirect_url_status, s.redirect_curl_errno, s.total_posts, s.total_comments, s.total_blogroll, s.total_time, s.total_bytes, s.rechecked, s.requests,';
1520
+ $fields .= 'l.loc_id, l.link_type, l.object_id, l.object_type, l.object_field, l.detected_at, l.anchor, l.raw_url, l.fragment, l.spaced, l.malformed, l.absolute, l.protorel, l.relative, l.nofollow, l.ignored, l.unlinked, l.modified, l.anchored, l.attributed';
1521
+
1522
+ // Prepare inner join
1523
+ $inner_join = ' INNER JOIN '.$wpdb->prefix.'wplnst_urls_status s ON u.url_id = s.url_id ';
1524
+
1525
+ // Prepare right join
1526
+ $right_join = ' RIGHT JOIN '.$wpdb->prefix.'wplnst_urls_locations l ON s.url_id = l.url_id AND s.scan_id = l.scan_id';
1527
+
1528
+ /* Prepare where */
1529
+
1530
+ // Base conditions
1531
+ $where = 's.scan_id = '.$scan_id.' AND u.url_id = s.url_id AND s.phase = "end"';
1532
+
1533
+ // Check URL id
1534
+ if (!empty($url_id)) {
1535
+ $where .= ' AND u.url_id = '.((int) $url_id);
1536
+
1537
+ // Check status level
1538
+ } elseif (isset($status_level) && false !== $status_level) {
1539
+ $where .= ' AND s.status_level = "'.esc_sql($status_level).'"';
1540
+
1541
+ // Check status code
1542
+ } elseif (isset($status_code) && false !== $status_code) {
1543
+ $where .= ' AND s.status_code = "'.esc_sql($status_code).'"';
1544
+ }
1545
+
1546
+ // Check object type
1547
+ if (isset($object_type) && false !== $object_type)
1548
+ $where .= ' AND l.object_type = "'.esc_sql($object_type).'"';
1549
+
1550
+ // Check object post type
1551
+ if (isset($object_post_type) && false !== $object_post_type)
1552
+ $where .= ' AND l.object_post_type = "'.esc_sql($object_post_type).'"';
1553
+
1554
+ // Check link type
1555
+ if (isset($link_type) && false !== $link_type)
1556
+ $where .= ' AND l.link_type = "'.esc_sql($link_type).'"';
1557
+
1558
+ // Check ignored type
1559
+ if (isset($ignored_type) && false !== $ignored_type) {
1560
+ if ('oir' == $ignored_type)
1561
+ $where .= ' AND l.ignored = 1';
1562
+ } else {
1563
+ $where .= ' AND l.ignored = 0';
1564
+ }
1565
+
1566
+ // SEO link type
1567
+ if (isset($seo_link_type) && false !== $seo_link_type)
1568
+ $where .= ' AND l.nofollow = '.(('nf' == $seo_link_type)? '1' : '0');
1569
+
1570
+ // Protocol type
1571
+ if (isset($protocol_type) && false !== $protocol_type)
1572
+ $where .= ('rel' == $protocol_type)? ' AND l.protorel = 1' : ' AND u.scheme = "'.esc_sql($protocol_type).'" AND l.protorel = 0';
1573
+
1574
+ // Special type
1575
+ if (isset($special_type) && false !== $special_type) {
1576
+ if ('rel' == $special_type) {
1577
+ $where .= ' AND l.relative = 1';
1578
+ } elseif ('abs' == $special_type) {
1579
+ $where .= ' AND l.absolute = 1';
1580
+ } elseif ('spa' == $special_type) {
1581
+ $where .= ' AND l.spaced = 1';
1582
+ } elseif ('mal' == $special_type) {
1583
+ $where .= ' AND l.malformed = 1';
1584
+ }
1585
+ }
1586
+
1587
+ // Action type
1588
+ if (isset($action_type) && false !== $action_type) {
1589
+ if ('unl' == $action_type) {
1590
+ $where .= ' AND l.unlinked = 1';
1591
+ } elseif ('mod' == $action_type) {
1592
+ $where .= ' AND (l.modified = 1 OR l.anchored = 1 OR l.attributed = 1)';
1593
+ } elseif ('umd' == $action_type) {
1594
+ $where .= ' AND l.modified = 0 AND l.anchored = 0 AND l.attributed = 0';
1595
+ } elseif ('rec' == $action_type) {
1596
+ $where .= ' AND s.rechecked = 1';
1597
+ }
1598
+ }
1599
+
1600
+ // Check destination type
1601
+ if (isset($dest_type) && false !== $dest_type && 'all' != $dest_type)
1602
+ $where .= ' AND u.scope = "'.esc_sql($dest_type).'"';
1603
+
1604
+ // Check search URL
1605
+ if (isset($search_url) && false !== $search_url && '' !== $search_url) {
1606
+ $like = esc_sql(addcslashes($search_url, '_%\\'));
1607
+ if ('r' == $search_url_type) {
1608
+ $where .= ' AND l.fragment LIKE "%'.$like.'%"';
1609
+ } else {
1610
+ $op = ('f' == $search_url_type)? ' = "'.esc_sql($search_url).'"' : ' LIKE "'.(('s' == $search_url_type || 'm' == $search_url_type)? '%' : '').$like.(('p' == $search_url_type || 'm' == $search_url_type)? '%' : '').'"';
1611
+ $where .= ' AND (u.url'.$op.' OR s.redirect_url'.$op.')';
1612
+ }
1613
+ }
1614
+
1615
+ // Check search anchor
1616
+ if (isset($search_anchor) && false !== $search_anchor && '' !== $search_anchor) {
1617
+ $op = ('f' == $search_anchor_type)? ' = "'.esc_sql($search_anchor).'"' : ' LIKE "'.(('s' == $search_anchor_type || 'm' == $search_anchor_type)? '%' : '').esc_sql(addcslashes($search_anchor, '_%\\')).(('p' == $search_anchor_type || 'm' == $search_anchor_type)? '%' : '').'"';
1618
+ $where .= ' AND l.anchor'.$op;
1619
+ }
1620
+
1621
+ // Check first order by date
1622
+ if (isset($order_type) && false !== $order_type) {
1623
+
1624
+ // Change default order
1625
+ if (in_array($order_type, array_keys(WPLNST_Core_Types::get_crawl_order()))) {
1626
+ $order_by = 'l.object_date_gmt '.(('asc' == $order_type)? 'ASC' : 'DESC');
1627
+
1628
+ // Orders
1629
+ } else {
1630
+ if ('dma' == $order_type) {
1631
+ $order_by = 'u.host ASC';
1632
+ } elseif ('dmd' == $order_type) {
1633
+ $order_by = 'u.host DESC';
1634
+ } elseif ('dta' == $order_type) {
1635
+ $order_by = 's.total_time ASC';
1636
+ } elseif ('dtd' == $order_type) {
1637
+ $order_by = 's.total_time DESC';
1638
+ } elseif ('dsa' == $order_type) {
1639
+ $order_by = 's.total_bytes ASC';
1640
+ } elseif ('dsd' == $order_type) {
1641
+ $order_by = 's.total_bytes DESC';
1642
+ }
1643
+ }
1644
+
1645
+ // Default order
1646
+ } else {
1647
+ $order_date = (isset($order_date) && in_array(strtoupper($order_date), array('ASC', 'DESC')))? strtoupper($order_date) : 'DESC';
1648
+ $order_by = 'l.object_date_gmt '.$order_date;
1649
+ }
1650
+
1651
+
1652
+ // Done
1653
+ return $this->pagination(array(
1654
+ 'paged' => $paged,
1655
+ 'per_page' => $per_page,
1656
+ 'sql' => 'SELECT $$$fields$$$ FROM '.$wpdb->prefix.'wplnst_urls AS u'.$inner_join.$right_join.' WHERE '.$where,
1657
+ 'fields' => $fields,
1658
+ 'order_by' => $order_by.', l.loc_id ASC',
1659
+ 'calc_rows' => wplnst_get_bsetting('mysql_calc_rows'),
1660
+ 'no_cache' => true,
1661
+ ));
1662
+ }
1663
+
1664
+
1665
+
1666
+ /**
1667
+ * Retrieve scan locations
1668
+ */
1669
+ public function get_scan_locations($args) {
1670
+
1671
+ // Globals
1672
+ global $wpdb;
1673
+
1674
+ // Check arguments
1675
+ if (empty($args) || !is_array($args))
1676
+ return false;
1677
+
1678
+ // Extract parameters
1679
+ extract($args);
1680
+
1681
+ // Check scan id
1682
+ $scan_id = empty($scan_id)? false : (int) $scan_id;
1683
+ if (empty($scan_id))
1684
+ return false;
1685
+
1686
+ // Check url id
1687
+ $url_id = empty($url_id)? false : (is_array($url_id)? array_map('intval', $url_id) : (int) $url_id);
1688
+ if (empty($url_id))
1689
+ return false;
1690
+
1691
+ // URL id where
1692
+ $url_id_where = is_array($url_id)? ' IN ('.implode(', ', $url_id).')' : ' = '.$url_id;
1693
+
1694
+ // Others
1695
+ $where = '';
1696
+
1697
+ // Check object type
1698
+ if (!empty($object_type))
1699
+ $where .= ' AND object_type = "'.esc_sql($object_type).'"';
1700
+
1701
+ // Check page
1702
+ $paged = empty($paged)? 1 : (int) $paged;
1703
+ if (empty($paged))
1704
+ $paged = 1;
1705
+
1706
+ // Check elements per page
1707
+ $per_page = isset($per_page)? (int) $per_page : (int) get_user_option('wplnst_scan_locations_per_page');
1708
+ if (empty($per_page))
1709
+ $per_page = 25;
1710
+
1711
+ // Done
1712
+ return $this->pagination(array(
1713
+ 'paged' => $paged,
1714
+ 'per_page' => $per_page,
1715
+ 'sql' => $wpdb->prepare('SELECT $$$fields$$$ FROM '.$wpdb->prefix.'wplnst_urls_locations WHERE url_id '.$url_id_where.' AND scan_id = %d '.$where, $scan_id),
1716
+ 'fields' => '*',
1717
+ 'order_by' => 'detected_at ASC',
1718
+ 'calc_rows' => wplnst_get_bsetting('mysql_calc_rows'),
1719
+ 'no_cache' => false,
1720
+ ));
1721
+ }
1722
+
1723
+
1724
+
1725
+ /**
1726
+ * Retrieve single scan location
1727
+ */
1728
+ public function get_scan_location_by_id($loc_id) {
1729
+
1730
+ // Global
1731
+ global $wpdb;
1732
+
1733
+ // Cast param
1734
+ $loc_id = (int) $loc_id;
1735
+
1736
+ // Perform query and return result
1737
+ $locations = $wpdb->get_results($wpdb->prepare('SELECT * FROM '.$wpdb->prefix.'wplnst_urls_locations WHERE loc_id = %d', $loc_id));
1738
+ return (!empty($locations) && is_array($locations) && 1 == count($locations))? $locations[0] : false;
1739
+ }
1740
+
1741
+
1742
+
1743
+ /**
1744
+ * Retrieve multiple scan locations
1745
+ */
1746
+ public function get_scan_locations_by_ids($loc_ids) {
1747
+
1748
+ // Global
1749
+ global $wpdb;
1750
+
1751
+ // Perform query and return result
1752
+ $locations = $wpdb->get_results('SELECT * FROM '.$wpdb->prefix.'wplnst_urls_locations WHERE loc_id IN ('.implode(', ', array_map('intval', $loc_ids)).')');
1753
+ return (empty($locations) || !is_array($locations))? false : $locations;
1754
+ }
1755
+
1756
+
1757
+
1758
+ /**
1759
+ * Retrieve array info of location marks
1760
+ */
1761
+ public function get_scan_location_marks($scan_id, $loc_id) {
1762
+
1763
+ // Global
1764
+ global $wpdb;
1765
+
1766
+ // Default
1767
+ $marks = array(
1768
+ 'url' => '',
1769
+ 'relative' => false,
1770
+ 'absolute' => false,
1771
+ 'spaced' => false,
1772
+ 'malformed' => false,
1773
+ 'https' => false,
1774
+ 'protorel' => false,
1775
+ 'redirs' => false,
1776
+ 'redirs_count' => '1 redirect',
1777
+ );
1778
+
1779
+ // Retrieve row
1780
+ $row = $wpdb->get_row($wpdb->prepare('SELECT u.url, u.scheme, l.relative, l.absolute, l.spaced, l.malformed, l.protorel, s.status_level, s.redirect_url_id, s.redirect_url, s.redirect_steps FROM '.$wpdb->prefix.'wplnst_urls AS u, '.$wpdb->prefix.'wplnst_urls_locations AS l, '.$wpdb->prefix.'wplnst_urls_status AS s WHERE l.loc_id = %d AND l.scan_id = %d AND u.url_id = l.url_id AND s.url_id = l.url_id AND s.scan_id = %d LIMIT 1', $loc_id, $scan_id, $scan_id));
1781
+ if (!empty($row) && is_object($row)) {
1782
+
1783
+ // Copy URL
1784
+ $marks['url'] = esc_html($row->url);
1785
+
1786
+ // Check HTTPS
1787
+ $marks['https'] = ('https' == $row->scheme);
1788
+
1789
+ // Link form
1790
+ $marks['relative'] = (1 == $row->relative);
1791
+ $marks['absolute'] = (1 == $row->absolute);
1792
+ $marks['spaced'] = (1 == $row->spaced);
1793
+ $marks['malformed'] = (1 == $row->malformed);
1794
+ $marks['protorel'] = (1 == $row->protorel);
1795
+
1796
+ // Redirs
1797
+ $marks['redirs'] = ('3' == $row->status_level && $row->redirect_url_id > 0 && !empty($row->redirect_url));
1798
+ if ($marks['redirs']) {
1799
+ $redirs_steps = @json_decode($row->redirect_steps, true);
1800
+ if (!empty($redirs_steps) && is_array($redirs_steps) && count($redirs_steps) > 1)
1801
+ $marks['redirs_count'] = count($redirs_steps).' redirects';
1802
+ }
1803
+ }
1804
+
1805
+ // Done
1806
+ return $marks;
1807
+ }
1808
+
1809
+
1810
+
1811
+ /**
1812
+ * Execute and extract pagination data
1813
+ * - paged
1814
+ * - per_page
1815
+ * - sql
1816
+ * - fields
1817
+ * - order_by
1818
+ * - calc_rows
1819
+ * - no_cache
1820
+ */
1821
+ public function pagination($args) {
1822
+
1823
+ // Globals
1824
+ global $wpdb;
1825
+
1826
+ // Check arguments
1827
+ if (empty($args) || !is_array($args))
1828
+ return false;
1829
+
1830
+ // Extract parameters
1831
+ extract($args);
1832
+
1833
+ // Check basic params
1834
+ if (empty($sql) || empty($fields))
1835
+ return false;
1836
+
1837
+ // Totals method and cache
1838
+ $calc_rows = !empty($calc_rows) && (true === $calc_rows);
1839
+ $no_cache = !empty($no_cache) && (true === $no_cache);
1840
+
1841
+ // Calculating offset
1842
+ $paged = empty($paged)? 1 : (int) $paged;
1843
+ $per_page = empty($per_page)? 10 : (int) $per_page;
1844
+ $offset = (int) (($paged - 1) * $per_page);
1845
+
1846
+ // Default
1847
+ $results = array(
1848
+ 'paged' => $paged,
1849
+ 'per_page' => $per_page,
1850
+ 'total_pages' => 0,
1851
+ 'rows_page' => 0,
1852
+ 'rows_start' => $offset + 1,
1853
+ 'rows_end' => 0,
1854
+ 'total_rows' => 0,
1855
+ 'calc_rows' => $calc_rows,
1856
+ 'no_cache' => $no_cache,
1857
+ 'rows' => array(),
1858
+ );
1859
+
1860
+ // Timer
1861
+ $time_start = microtime(true);
1862
+
1863
+ // Count rows method
1864
+ if (!$calc_rows) {
1865
+
1866
+ // Prepare COUNT sql
1867
+ $sql_count = str_replace('$$$fields$$$', 'COUNT(*)', $sql);
1868
+ if ($no_cache)
1869
+ $sql_count = str_replace('SELECT ', 'SELECT SQL_NO_CACHE ', $sql_count);
1870
+
1871
+ // Obtain totals
1872
+ $results['total_rows'] = (int) $wpdb->get_var($sql_count);
1873
+
1874
+ // Calc
1875
+ } else {
1876
+
1877
+ // Replace SELECT
1878
+ $sql = str_replace('SELECT ', 'SELECT SQL_CALC_FOUND_ROWS ', $sql);
1879
+ }
1880
+
1881
+ // Check NO CACHE
1882
+ if ($no_cache)
1883
+ $sql = str_replace('SELECT ', 'SELECT SQL_NO_CACHE ', $sql);
1884
+
1885
+ // Set fields
1886
+ $sql = str_replace('$$$fields$$$', $fields, $sql);
1887
+
1888
+ // Add ORDER and LIMIT
1889
+ $sql .= (empty($order_by)? '' : ' ORDER BY '.$order_by).' LIMIT '.$offset.', '.$per_page;
1890
+ //echo $sql;
1891
+ // Perform query
1892
+ $results['rows'] = $wpdb->get_results($sql);
1893
+
1894
+ // Calc rows
1895
+ if ($calc_rows)
1896
+ $results['total_rows'] = (int) $wpdb->get_var('SELECT FOUND_ROWS()');
1897
+
1898
+ // End timer
1899
+ $results['time'] = microtime(true) - $time_start;
1900
+ //echo '<br />'.$results['time'].' s';
1901
+ // Total pages
1902
+ $results['total_pages'] = empty($results['total_rows'])? 0 : ceil($results['total_rows'] / $per_page);
1903
+
1904
+ // Queried rows
1905
+ if (!empty($results['rows']) && is_array($results['rows'])) {
1906
+
1907
+ // Total rows in page
1908
+ $results['rows_page'] = count($results['rows']);
1909
+ $results['rows_end'] = $results['rows_start'] + $results['rows_page'] - 1;
1910
+ }
1911
+
1912
+ // Done
1913
+ return (object) $results;
1914
+ }
1915
+
1916
+
1917
+
1918
+ }
core/scheme.php ADDED
@@ -0,0 +1,288 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * WP Link Status Core Scheme class
5
+ *
6
+ * @package WP Link Status
7
+ * @subpackage WP Link Status Core
8
+ */
9
+ class WPLNST_Core_Scheme {
10
+
11
+
12
+
13
+ /**
14
+ * Tables used by this plugin
15
+ */
16
+ public static function get_tables() {
17
+ return array(
18
+ 'urls',
19
+ 'urls_locations',
20
+ 'urls_locations_att',
21
+ 'urls_status',
22
+ 'scans',
23
+ 'scans_objects',
24
+ );
25
+ }
26
+
27
+
28
+
29
+ /**
30
+ * Remove all plugin tables
31
+ */
32
+ public static function drop_tables() {
33
+
34
+ // Globals
35
+ global $wpdb;
36
+
37
+ // Plugin tables
38
+ $tables = self::get_tables();
39
+
40
+ // Remove each one
41
+ foreach ($tables as $name)
42
+ $wpdb->query('DROP TABLE '.$wpdb->prefix.'wplnst_'.esc_sql($name));
43
+ }
44
+
45
+
46
+
47
+ /**
48
+ * Check plugin custom tables
49
+ */
50
+ public static function check_tables() {
51
+
52
+ // Globals
53
+ global $wpdb;
54
+
55
+ // Initialize
56
+ $create = array();
57
+
58
+ // Plugin tables
59
+ $tables = self::get_tables();
60
+
61
+ // Check each table
62
+ foreach ($tables as $name) {
63
+ $result = $wpdb->get_var('SHOW TABLES LIKE "'.$wpdb->prefix.'wplnst_'.esc_sql($name).'"');
64
+ if (empty($result))
65
+ $create[] = $name;
66
+ }
67
+
68
+ // Done
69
+ return empty($create)? false : $create;
70
+ }
71
+
72
+
73
+
74
+ /**
75
+ * Create custom plugin tables
76
+ */
77
+ public static function create_tables($tables = array()) {
78
+
79
+ // Globals
80
+ global $wpdb;
81
+
82
+ // Compose charset
83
+ $charset = (empty($wpdb->charset)? '' : ' DEFAULT CHARACTER SET '.$wpdb->charset).(empty($wpdb->collate)? '' : ' COLLATE '.$wpdb->collate);
84
+
85
+ // URLs table
86
+ if (in_array('urls', $tables)) {
87
+ $wpdb->query('CREATE TABLE IF NOT EXISTS `'.$wpdb->prefix.'wplnst_urls` (
88
+ `url_id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
89
+ `url` TEXT CHARACTER SET utf8 COLLATE utf8_bin NOT NULL DEFAULT "",
90
+ `hash` VARCHAR(64) NOT NULL DEFAULT "",
91
+ `scheme` VARCHAR(20) NOT NULL DEFAULT "",
92
+ `host` VARCHAR(255) NOT NULL DEFAULT "",
93
+ `path` VARCHAR(255) NOT NULL DEFAULT "",
94
+ `query` VARCHAR(255) NOT NULL DEFAULT "",
95
+ `scope` VARCHAR(10) NOT NULL DEFAULT "",
96
+ `created_at` DATETIME NOT NULL DEFAULT "0000-00-00 00:00:00",
97
+ `last_scan_id` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
98
+ `last_status_level` VARCHAR(1) NOT NULL DEFAULT "",
99
+ `last_status_code` VARCHAR(3) NOT NULL DEFAULT "",
100
+ `last_curl_errno` INT(3) UNSIGNED NOT NULL DEFAULT 0,
101
+ `last_request_at` DATETIME NOT NULL DEFAULT "0000-00-00 00:00:00",
102
+ PRIMARY KEY (`url_id`),
103
+ KEY `url` (`url`(255)),
104
+ UNIQUE KEY `hash` (`hash`),
105
+ KEY `scheme` (`scheme`),
106
+ KEY `host` (`host`),
107
+ KEY `path` (`path`),
108
+ KEY `query` (`query`),
109
+ KEY `scope` (`scope`),
110
+ KEY `last_scan_id` (`last_scan_id`),
111
+ KEY `last_status_level` (`last_status_level`),
112
+ KEY `last_status_code` (`last_status_code`),
113
+ KEY `last_curl_errno` (`last_curl_errno`),
114
+ KEY `last_request_at` (`last_request_at`)
115
+ )'.$charset);
116
+ }
117
+
118
+ // URLs and locations relationship table
119
+ if (in_array('urls_locations', $tables)) {
120
+ $wpdb->query('CREATE TABLE IF NOT EXISTS `'.$wpdb->prefix.'wplnst_urls_locations` (
121
+ `loc_id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
122
+ `url_id` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
123
+ `scan_id` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
124
+ `link_type` VARCHAR(25) NOT NULL DEFAULT "",
125
+ `object_id` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
126
+ `object_type` VARCHAR(50) NOT NULL DEFAULT "",
127
+ `object_post_type` VARCHAR(20) NOT NULL DEFAULT "",
128
+ `object_field` VARCHAR(100) NOT NULL DEFAULT "",
129
+ `object_date_gmt` DATETIME NOT NULL DEFAULT "0000-00-00 00:00:00",
130
+ `detected_at` DATETIME NOT NULL DEFAULT "0000-00-00 00:00:00",
131
+ `chunk` TEXT NOT NULL DEFAULT "",
132
+ `anchor` TEXT NOT NULL DEFAULT "",
133
+ `raw_url` TEXT NOT NULL DEFAULT "",
134
+ `fragment` TEXT NOT NULL DEFAULT "",
135
+ `spaced` TINYINT(1) NOT NULL DEFAULT 0,
136
+ `malformed` TINYINT(1) NOT NULL DEFAULT 0,
137
+ `absolute` TINYINT(1) NOT NULL DEFAULT 0,
138
+ `protorel` TINYINT(1) NOT NULL DEFAULT 0,
139
+ `relative` TINYINT(1) NOT NULL DEFAULT 0,
140
+ `nofollow` TINYINT(1) NOT NULL DEFAULT 0,
141
+ `ignored` TINYINT(1) NOT NULL DEFAULT 0,
142
+ `unlinked` TINYINT(1) NOT NULL DEFAULT 0,
143
+ `modified` TINYINT(1) NOT NULL DEFAULT 0,
144
+ `anchored` TINYINT(1) NOT NULL DEFAULT 0,
145
+ `attributed` TINYINT(1) NOT NULL DEFAULT 0,
146
+ PRIMARY KEY (`loc_id`),
147
+ KEY `url_id` (`url_id`),
148
+ KEY `scan_id` (`scan_id`),
149
+ KEY `link_type` (`link_type`),
150
+ KEY `object_id` (`object_id`),
151
+ KEY `object_type` (`object_type`),
152
+ KEY `object_date_gmt` (`object_date_gmt`),
153
+ KEY `anchor` (`anchor`(255)),
154
+ KEY `spaced` (`spaced`),
155
+ KEY `malformed` (`malformed`),
156
+ KEY `absolute` (`absolute`),
157
+ KEY `protorel` (`protorel`),
158
+ KEY `relative` (`relative`),
159
+ KEY `nofollow` (`nofollow`),
160
+ KEY `ignored` (`ignored`),
161
+ KEY `unlinked` (`unlinked`),
162
+ KEY `modified` (`modified`),
163
+ KEY `anchored` (`anchored`),
164
+ KEY `attributed` (`attributed`)
165
+ )'.$charset);
166
+ }
167
+
168
+ // URLs locations and attributes relationship table
169
+ if (in_array('urls_locations_att', $tables)) {
170
+ $wpdb->query('CREATE TABLE IF NOT EXISTS `'.$wpdb->prefix.'wplnst_urls_locations_att` (
171
+ `att_id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
172
+ `loc_id` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
173
+ `scan_id` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
174
+ `attribute` VARCHAR(255) NOT NULL DEFAULT "",
175
+ `value` VARCHAR(255) NOT NULL DEFAULT "",
176
+ PRIMARY KEY (`att_id`),
177
+ KEY `loc_id` (`loc_id`),
178
+ KEY `scan_id` (`scan_id`),
179
+ KEY `attribute` (`attribute`),
180
+ KEY `value` (`value`)
181
+ )'.$charset);
182
+ }
183
+
184
+ // URLs status and scans relationship table
185
+ if (in_array('urls_status', $tables)) {
186
+ $wpdb->query('CREATE TABLE IF NOT EXISTS `'.$wpdb->prefix.'wplnst_urls_status` (
187
+ `url_id` BIGINT(20) UNSIGNED NOT NULL,
188
+ `scan_id` BIGINT(20) UNSIGNED NOT NULL,
189
+ `status_level` VARCHAR(1) NOT NULL DEFAULT "",
190
+ `status_code` VARCHAR(3) NOT NULL DEFAULT "",
191
+ `curl_errno` INT(3) UNSIGNED NOT NULL DEFAULT 0,
192
+ `redirect_url` TEXT NOT NULL DEFAULT "",
193
+ `redirect_steps` TEXT NOT NULL DEFAULT "",
194
+ `redirect_url_id` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
195
+ `redirect_url_status` VARCHAR(3) NOT NULL DEFAULT "",
196
+ `redirect_curl_errno` INT(3) UNSIGNED NOT NULL DEFAULT 0,
197
+ `headers` TEXT NOT NULL DEFAULT "",
198
+ `headers_request` TEXT NOT NULL DEFAULT "",
199
+ `body` TEXT NOT NULL DEFAULT "",
200
+ `phase` VARCHAR(20) NOT NULL DEFAULT "",
201
+ `created_at` DATETIME NOT NULL DEFAULT "0000-00-00 00:00:00",
202
+ `started_at` DATETIME NOT NULL DEFAULT "0000-00-00 00:00:00",
203
+ `request_at` DATETIME NOT NULL DEFAULT "0000-00-00 00:00:00",
204
+ `total_objects` INT(9) UNSIGNED NOT NULL DEFAULT 0,
205
+ `total_posts` INT(9) UNSIGNED NOT NULL DEFAULT 0,
206
+ `total_comments` INT(9) UNSIGNED NOT NULL DEFAULT 0,
207
+ `total_blogroll` INT(9) UNSIGNED NOT NULL DEFAULT 0,
208
+ `total_time` DECIMAL(3,3) UNSIGNED NOT NULL DEFAULT 0,
209
+ `total_bytes` BIGINT(20) UNSIGNED NOT NULL,
210
+ `requests` INT(4) UNSIGNED NOT NULL DEFAULT 0,
211
+ `rechecked` TINYINT(1) NOT NULL DEFAULT 0,
212
+ PRIMARY KEY (`url_id`, `scan_id`),
213
+ KEY `url_id` (`url_id`),
214
+ KEY `scan_id` (`scan_id`),
215
+ KEY `status_level` (`status_level`),
216
+ KEY `status_code` (`status_code`),
217
+ KEY `curl_errno` (`curl_errno`),
218
+ KEY `redirect_url_id` (`redirect_url_id`),
219
+ KEY `redirect_url_status` (`redirect_url_status`),
220
+ KEY `phase` (`phase`),
221
+ KEY `total_objects` (`total_objects`),
222
+ KEY `total_posts` (`total_posts`),
223
+ KEY `total_comments` (`total_comments`),
224
+ KEY `total_blogroll` (`total_blogroll`),
225
+ KEY `total_time` (`total_time`),
226
+ KEY `total_bytes` (`total_bytes`),
227
+ KEY `rechecked` (`rechecked`)
228
+ )'.$charset);
229
+ }
230
+
231
+ // Scans table
232
+ if (in_array('scans', $tables)) {
233
+ $wpdb->query('CREATE TABLE IF NOT EXISTS `'.$wpdb->prefix.'wplnst_scans` (
234
+ `scan_id` BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT,
235
+ `type` VARCHAR(20) NOT NULL DEFAULT "scan",
236
+ `name` VARCHAR(255) NOT NULL DEFAULT "",
237
+ `status` VARCHAR(20) NOT NULL DEFAULT "",
238
+ `ready` TINYINT(1) NOT NULL DEFAULT 0,
239
+ `hash` VARCHAR(32) NOT NULL DEFAULT "",
240
+ `created_at` DATETIME NOT NULL DEFAULT "0000-00-00 00:00:00",
241
+ `modified_at` DATETIME NOT NULL DEFAULT "0000-00-00 00:00:00",
242
+ `modified_by` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
243
+ `started_at` DATETIME NOT NULL DEFAULT "0000-00-00 00:00:00",
244
+ `enqueued_at` DATETIME NOT NULL DEFAULT "0000-00-00 00:00:00",
245
+ `stopped_at` DATETIME NOT NULL DEFAULT "0000-00-00 00:00:00",
246
+ `continued_at` DATETIME NOT NULL DEFAULT "0000-00-00 00:00:00",
247
+ `finished_at` DATETIME NOT NULL DEFAULT "0000-00-00 00:00:00",
248
+ `config` TEXT NOT NULL DEFAULT "",
249
+ `summary` TEXT NOT NULL DEFAULT "",
250
+ `trace` TEXT NOT NULL DEFAULT "",
251
+ `threads` TEXT NOT NULL DEFAULT "",
252
+ `max_threads` INT(3) UNSIGNED NOT NULL DEFAULT 0,
253
+ `connect_timeout` INT(4) UNSIGNED NOT NULL DEFAULT 0,
254
+ `request_timeout` INT(4) UNSIGNED NOT NULL DEFAULT 0,
255
+ PRIMARY KEY (`scan_id`),
256
+ KEY `type` (`type`),
257
+ KEY `name` (`name`),
258
+ KEY `status` (`status`),
259
+ UNIQUE KEY `hash` (`hash`),
260
+ KEY `config` (`config`(255))
261
+ )'.$charset);
262
+ }
263
+
264
+ // Scans objects
265
+ if (in_array('scans_objects', $tables)) {
266
+ $wpdb->query('CREATE TABLE IF NOT EXISTS `'.$wpdb->prefix.'wplnst_scans_objects` (
267
+ `scan_id` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
268
+ `object_id` BIGINT(20) UNSIGNED NOT NULL DEFAULT 0,
269
+ `object_type` VARCHAR(50) NOT NULL DEFAULT "",
270
+ `object_date_gmt` DATETIME NOT NULL DEFAULT "0000-00-00 00:00:00",
271
+ PRIMARY KEY (`scan_id`, `object_id`, `object_type`),
272
+ KEY `scan_id` (`scan_id`),
273
+ KEY `object_type` (`object_type`),
274
+ KEY `object_date_gmt` (`object_date_gmt`)
275
+ )'.$charset);
276
+ }
277
+ }
278
+
279
+
280
+
281
+ /**
282
+ * Upgrade table schemes
283
+ */
284
+ public static function upgrade() {}
285
+
286
+
287
+
288
+ }
core/settings.php ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * WP Link Status Core Settings class
5
+ *
6
+ * @package WP Link Status
7
+ * @subpackage WP Link Status Core
8
+ */
9
+ class WPLNST_Core_Settings {
10
+
11
+
12
+
13
+ /**
14
+ * Return a numeric setting
15
+ */
16
+ public static function get_nsetting($name, $value = 0) {
17
+
18
+ // Load settings
19
+ static $settings;
20
+ if (!isset($settings))
21
+ $settings = self::get_default_nsettings();
22
+
23
+ // Check available
24
+ if (!isset($settings[$name]))
25
+ return false;
26
+
27
+ // Check boundary
28
+ if ('min' === $value || 'max' === $value)
29
+ return $settings[$name][$value];
30
+
31
+ // Check input value
32
+ $value = (int) $value;
33
+ if (empty($value))
34
+ $value = (int) get_option('wplnst_'.$name);
35
+
36
+ // Check return value
37
+ $setting = $settings[$name];
38
+ return (empty($value) || $value < $setting['min'] || $value > $setting['max'])? $setting['default'] : $value;
39
+ }
40
+
41
+
42
+
43
+ /**
44
+ * Return an array of default numeric settings
45
+ */
46
+ private static function get_default_nsettings() {
47
+ return array(
48
+ 'max_threads' => array('min' => 1, 'max' => 999, 'default' => 1),
49
+ 'max_scans' => array('min' => 1, 'max' => 999, 'default' => 1),
50
+ 'max_pack' => array('min' => 1, 'max' => 999, 'default' => 25),
51
+ 'max_requests' => array('min' => 1, 'max' => 999, 'default' => 3),
52
+ 'max_redirs' => array('min' => 1, 'max' => 999, 'default' => 5),
53
+ 'max_download' => array('min' => 32, 'max' => 10240, 'default' => 2048),
54
+ 'connect_timeout' => array('min' => 1, 'max' => 999, 'default' => 10),
55
+ 'request_timeout' => array('min' => 1, 'max' => 999, 'default' => 30),
56
+ 'extra_timeout' => array('min' => 5, 'max' => 999, 'default' => 5),
57
+ 'crawler_alive' => array('min' => 3, 'max' => 999, 'default' => 30),
58
+ 'total_objects' => array('min' => 30, 'max' => 300, 'default' => 120),
59
+ 'summary_status' => array('min' => 10, 'max' => 999, 'default' => 30),
60
+ 'summary_phases' => array('min' => 10, 'max' => 999, 'default' => 30),
61
+ 'summary_objects' => array('min' => 10, 'max' => 999, 'default' => 30),
62
+ 'recursion_limit' => array('min' => 10, 'max' => 99999, 'default' => 99),
63
+ );
64
+ }
65
+
66
+
67
+
68
+ /**
69
+ * Return a boolean setting
70
+ */
71
+ public static function get_bsetting($name, $use_default_if_empty = true) {
72
+
73
+ // Check stored value
74
+ $value = ''.get_option('wplnst_'.$name);
75
+ if ('' !== $value || !$use_default_if_empty)
76
+ return ('on' == $value);
77
+
78
+ // Load settings
79
+ static $settings;
80
+ if (!isset($settings))
81
+ $settings = self::get_default_bsettings();
82
+
83
+ // Return default setting or false
84
+ return isset($settings[$name])? ('on' == $settings[$name]) : false;
85
+ }
86
+
87
+
88
+
89
+ /**
90
+ * Return an array of default boolean settings
91
+ */
92
+ private static function get_default_bsettings() {
93
+ return array(
94
+ 'mysql_calc_rows' => 'off',
95
+ 'uninstall_data' => 'off',
96
+ );
97
+ }
98
+
99
+
100
+
101
+ /**
102
+ * Return a text setting
103
+ */
104
+ public static function get_tsetting($name, $use_default_if_empty = true) {
105
+
106
+ // Check stored value
107
+ $value = ''.get_option('wplnst_'.$name);
108
+ if ('' !== $value || !$use_default_if_empty)
109
+ return $value;
110
+
111
+ // Load settings
112
+ static $settings;
113
+ if (!isset($settings))
114
+ $settings = self::get_default_tsettings();
115
+
116
+ // Return default setting or false
117
+ return isset($settings[$name])? $settings[$name] : false;
118
+ }
119
+
120
+
121
+
122
+ /**
123
+ * Return an array of default string settings
124
+ */
125
+ private static function get_default_tsettings() {
126
+ return array(
127
+ 'user_agent' => 'Mozilla/5.0 (Windows NT 6.1; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0',
128
+ );
129
+ }
130
+
131
+
132
+
133
+ /**
134
+ * Remove all plugin options
135
+ */
136
+ public static function delete_all_options() {
137
+
138
+ // Collect numeric, text, and crawler options
139
+ $options = array_merge(
140
+ array_keys(self::get_default_nsettings()),
141
+ array_keys(self::get_default_tsettings()),
142
+ array_keys(self::get_default_bsettings()),
143
+ self::get_crawler_options_names()
144
+ );
145
+
146
+ // Remove all plugin settings
147
+ foreach ($options as $name)
148
+ delete_option('wplnst_'.$name);
149
+ }
150
+
151
+
152
+
153
+ /**
154
+ * Remove custom options
155
+ */
156
+ public static function delete_crawler_options() {
157
+ $options = self::get_crawler_options_names();
158
+ foreach ($options as $name)
159
+ delete_option('wplnst_'.$name);
160
+ }
161
+
162
+
163
+
164
+ /**
165
+ * Retrive custom options names
166
+ */
167
+ private static function get_crawler_options_names() {
168
+ return array('crawler_timestamp', 'crawler_slug', 'crawler_notifications');
169
+ }
170
+
171
+
172
+
173
+ }
core/status.php ADDED
@@ -0,0 +1,212 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * WP Link Status Core Status class
5
+ *
6
+ * @package WP Link Status
7
+ * @subpackage WP Link Status Core
8
+ */
9
+ class WPLNST_Core_Status {
10
+
11
+
12
+
13
+ /**
14
+ * Input data
15
+ */
16
+ public $data;
17
+
18
+
19
+
20
+ /**
21
+ * Pairs name/value
22
+ */
23
+ public $headers = array();
24
+ public $headers_request = array();
25
+
26
+
27
+
28
+ /**
29
+ * Status code, description and level
30
+ */
31
+ public $code = 0;
32
+ public $code_desc = '';
33
+ public $level = 0;
34
+
35
+
36
+
37
+ /**
38
+ * Redirect URL
39
+ */
40
+ public $redirect_url = '';
41
+ public $redirect_url_id = 0;
42
+ public $redirect_url_level = 0;
43
+ public $redirect_url_status = '';
44
+ public $redirect_url_status_desc = '';
45
+ public $redirect_curl_errno = 0;
46
+ public $redirect_curl_err_title = '';
47
+ public $redirect_curl_err_desc = '';
48
+ public $redirect_steps = '';
49
+
50
+
51
+
52
+ /**
53
+ * cURL error number and desc
54
+ */
55
+ public $curl_errno = 0;
56
+ public $curl_err_title = '';
57
+ public $curl_err_desc = '';
58
+
59
+
60
+
61
+ /**
62
+ * Exact request timestamp
63
+ */
64
+ public $timestamp = 0;
65
+
66
+
67
+
68
+ /**
69
+ * Timestamp to datetime
70
+ */
71
+ public $request_at = '0000-00-00 00:00:00';
72
+
73
+
74
+
75
+ /**
76
+ * Total request time
77
+ */
78
+ public $total_time = 0;
79
+
80
+
81
+
82
+ /**
83
+ * Total bytes and size
84
+ */
85
+ public $total_size = '';
86
+ public $total_bytes = 0;
87
+
88
+
89
+
90
+ // Initialization
91
+ // ---------------------------------------------------------------------------------------------------
92
+
93
+
94
+
95
+ /**
96
+ * Constructor
97
+ */
98
+ public function __construct($data = null) {
99
+ if (!empty($data) && is_array($data))
100
+ $this->process_data($data);
101
+ }
102
+
103
+
104
+
105
+ // Data extract and validation
106
+ // ---------------------------------------------------------------------------------------------------
107
+
108
+
109
+
110
+ /**
111
+ * Process request data
112
+ */
113
+ public function process_data($data) {
114
+
115
+ // Copy var
116
+ $this->data = $data;
117
+
118
+ // Process headers
119
+ if (!empty($data['headers']))
120
+ $this->extract_headers($data['headers']);
121
+
122
+ // Process request headers
123
+ if (!empty($data['headers_request']))
124
+ $this->extract_headers_request($data['headers_request']);
125
+
126
+ // Check cURL error
127
+ if (!empty($data['curl_errno']))
128
+ $this->curl_errno = (int) $data['curl_errno'];
129
+
130
+ // Timestamp and DateTime
131
+ if (!empty($data['timestamp'])) {
132
+ $this->timestamp = (int) $data['timestamp'];
133
+ if (!empty($this->timestamp) && false !== ($datetime = @gmdate('Y-m-d H:i:s', $this->timestamp)))
134
+ $this->request_at = $datetime;
135
+ }
136
+
137
+ // Total time
138
+ if (!empty($data['total_time']))
139
+ $this->total_time = (float) $data['total_time'];
140
+
141
+ // Total bytes
142
+ if (!empty($data['total_bytes']))
143
+ $this->total_bytes = (int) $data['total_bytes'];
144
+ }
145
+
146
+
147
+
148
+ /**
149
+ * Parse and extract headers data
150
+ */
151
+ private function extract_headers($headers) {
152
+
153
+ // Parse headers
154
+ $headers_raw = explode("\n", str_replace("\n\r", "\n", $headers));
155
+ foreach ($headers_raw as $header) {
156
+
157
+ // Clean line
158
+ $header = trim($header);
159
+ if (empty($header))
160
+ continue;
161
+
162
+ // Check redirection in status code
163
+ if (!isset($status_code) && 0 === stripos($header, 'HTTP/')) {
164
+ $line = trim(preg_replace('/\s+/', ' ', $header));
165
+ $line = explode(' ', $line);
166
+ if (count($line) > 1) {
167
+ $status_code = (int) mb_substr(trim($line[1]), 0, 3);
168
+ $this->headers['status'] = $header;
169
+ }
170
+
171
+ // Parse other headers
172
+ } elseif (false !== ($pos = strpos($header, ':')) && $pos > 0) {
173
+ $name = trim(mb_substr($header, 0, $pos));
174
+ $this->headers[$name] = trim(mb_substr($header, $pos + 1));
175
+ if ('location' == strtolower($name))
176
+ $this->redirect_url = $this->headers[$name];
177
+ }
178
+ }
179
+
180
+ // Check status
181
+ if (isset($status_code)) {
182
+ $this->code = $status_code;
183
+ $this->level = (int) mb_substr($status_code, 0 , 1);
184
+ }
185
+ }
186
+
187
+
188
+
189
+ /**
190
+ * Parse and extract request headers data
191
+ */
192
+ private function extract_headers_request($headers) {
193
+
194
+ // Parse headers
195
+ $headers_raw = explode("\n", str_replace("\n\r", "\n", $headers));
196
+ foreach ($headers_raw as $header) {
197
+
198
+ // Check GET method
199
+ if (0 === strpos($header, 'GET ')) {
200
+ $this->headers_request['GET'] = mb_substr($header, 4);
201
+
202
+ // Check property
203
+ } elseif ((false !== ($pos = strpos($header, ':')) && $pos > 0)) {
204
+ $name = trim(mb_substr($header, 0, $pos));
205
+ $this->headers_request[$name] = trim(mb_substr($header, $pos + 1));
206
+ }
207
+ }
208
+ }
209
+
210
+
211
+
212
+ }
core/text.php ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * WP Link Status Core Text class
5
+ *
6
+ * @package WP Link Status
7
+ * @subpackage WP Link Status Core
8
+ */
9
+ class WPLNST_Core_Text {
10
+
11
+
12
+
13
+ /**
14
+ * Translate on the fly (not stored) by name
15
+ */
16
+ public static function get_text($name) {
17
+
18
+ // Static
19
+ static $names = array();
20
+
21
+ // Check cache
22
+ if (isset($names[$name]))
23
+ return $names[$name];
24
+
25
+ // Initialize
26
+ $text = false;
27
+
28
+ // Check key
29
+ switch($name) {
30
+
31
+ case 'scans':
32
+ $text = __('Scans', 'wplnst');
33
+ break;
34
+
35
+ case 'scan_new':
36
+ $text = __('New scan', 'wplnst');
37
+ break;
38
+
39
+ case 'scan_new_add':
40
+ $text = __('Add new scan', 'wplnst');
41
+ break;
42
+
43
+ case 'scan_edit':
44
+ $text = __('Edit scan', 'wplnst');
45
+ break;
46
+
47
+ case 'scan_delete':
48
+ $text = __('Delete scan', 'wplnst');
49
+ break;
50
+
51
+ case 'scan_delete_confirm':
52
+ $text = __('Do you want to remove this scan?', 'wplnst');
53
+ break;
54
+
55
+ case 'crawler_action':
56
+ $text = __('Crawler action', 'wplnst');
57
+ break;
58
+
59
+ case 'crawler_results':
60
+ $text = __('Crawler results', 'wplnst');
61
+ break;
62
+
63
+ case 'settings':
64
+ $text = __('Settings', 'wplnst');
65
+ break;
66
+
67
+ case 'max_scans':
68
+ $text = sprintf(__('Sorry, maximum of <strong>%d running scans</strong> achieved and the scan has been enqueued, it will be launched as soon as possible.', 'wplnst'), wplnst_get_nsetting('max_scans'));
69
+ break;
70
+
71
+ case 'scan_not_found':
72
+ $text = sprintf(__('Sorry, scan not found. Please go to the <a href="%s">scans list screen</a> and select another scan.', 'wplnst'), esc_url(WPLNST_Core_Plugin::get_url_scans()));
73
+ break;
74
+
75
+ case 'invalid_data':
76
+ $text = __('Sorry, <strong>invalid form data</strong>. Please, <a href="javascript:history.back();">go back</a>, reload the last page and try again.', 'wplnst');
77
+ break;
78
+
79
+ case 'invalid_nonce':
80
+ $text = __('Sorry, <strong>invalid security data</strong>. Please, <a href="javascript:history.back();">go back</a>, reload the last page and try again.', 'wplnst');
81
+ break;
82
+
83
+ case 'server_comm_error':
84
+ $text = __('Server communication error', 'wplnst');
85
+ break;
86
+
87
+ case 'unknown_error':
88
+ $text = __('Unknown error', 'wplnst');
89
+ break;
90
+
91
+ case 'no_salt':
92
+ $text = __('Unable to create the salt file needed to improve crawler security. Please check your <strong>wp content directory permissions</strong> allowing to save files.', 'wplnst');
93
+ break;
94
+ }
95
+
96
+ // Save
97
+ $names[$name] = $text;
98
+
99
+ // Done
100
+ return $text;
101
+ }
102
+
103
+
104
+
105
+ }
core/types-curl.php ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * WP Link Status Core Types CURL class
5
+ *
6
+ * @package WP Link Status
7
+ * @subpackage WP Link Status Core
8
+ */
9
+ class WPLNST_Core_Types_CURL {
10
+
11
+
12
+
13
+ /**
14
+ * Retrieve info for a given error code
15
+ */
16
+ public static function get_code_info($num) {
17
+ $codes = self::get_codes();
18
+ return isset($codes[$num])? $codes[$num] : false;
19
+ }
20
+
21
+
22
+
23
+ /**
24
+ * Retrieve all error codes
25
+ */
26
+ public static function get_codes() {
27
+ static $codes;
28
+ if (!isset($codes))
29
+ $codes = self::get_codes_array();
30
+ return $codes;
31
+ }
32
+
33
+
34
+
35
+ /**
36
+ * Retrieve array of error codes and descriptions
37
+ * http://curl.haxx.se/libcurl/c/libcurl-errors.html
38
+ */
39
+ private static function get_codes_array() {
40
+ return array(
41
+ 0 => array("code" => "CURLE_OK", "title" => __("Ok", "wplnst"), "desc" => __("All fine. Proceed as usual.", "wplnst")),
42
+ 1 => array("code" => "CURLE_UNSUPPORTED_PROTOCOL", "title" => __("Unsupported protocol", "wplnst"), "desc" => __("The URL you passed to libcurl used a protocol that this libcurl does not support. The support might be a compile-time option that you didn't use, it can be a misspelled protocol string or just a protocol libcurl has no code for.", "wplnst")),
43
+ 2 => array("code" => "CURLE_FAILED_INIT", "title" => __("Failed initialization", "wplnst"), "desc" => __("Very early initialization code failed. This is likely to be an internal error or problem, or a resource problem where something fundamental couldn't get done at init time.", "wplnst")),
44
+ 3 => array("code" => "CURLE_URL_MALFORMAT", "title" => __("URL malformat", "wplnst"), "desc" => __("The URL was not properly formatted.", "wplnst")),
45
+ 4 => array("code" => "CURLE_NOT_BUILT_IN", "title" => __("Not built-in", "wplnst"), "desc" => __("A requested feature, protocol or option was not found built-in in this libcurl due to a build-time decision. This means that a feature or option was not enabled or explicitly disabled when libcurl was built and in order to get it to function you have to get a rebuilt libcurl.", "wplnst")),
46
+ 5 => array("code" => "CURLE_COULDNT_RESOLVE_PROXY", "title" => __("Couldn't resolve proxy", "wplnst"), "desc" => __("The given proxy host could not be resolved. ", "wplnst")),
47
+ 6 => array("code" => "CURLE_COULDNT_RESOLVE_HOST", "title" => __("Couldn't resolve host", "wplnst"), "desc" => __("The given remote host was not resolved.", "wplnst")),
48
+ 7 => array("code" => "CURLE_COULDNT_CONNECT", "title" => __("Couldn't connect", "wplnst"), "desc" => __("Failed to connect() to host or proxy.", "wplnst")),
49
+ 8 => array("code" => "CURLE_FTP_WEIRD_SERVER_REPLY", "title" => __("FTP weird server reply", "wplnst"), "desc" => __("After connecting to a FTP server, libcurl expects to get a certain reply back. This error code implies that it got a strange or bad reply. The given remote server is probably not an OK FTP server.", "wplnst")),
50
+ 9 => array("code" => "CURLE_REMOTE_ACCESS_DENIED", "title" => __("Remote access denied", "wplnst"), "desc" => __("We were denied access to the resource given in the URL. For FTP, this occurs while trying to change to the remote directory.", "wplnst")),
51
+ 10 => array("code" => "CURLE_FTP_ACCEPT_FAILED", "title" => __("FTP access failed", "wplnst"), "desc" => __("While waiting for the server to connect back when an active FTP session is used, an error code was sent over the control connection or similar.", "wplnst")),
52
+ 11 => array("code" => "CURLE_FTP_WEIRD_PASS_REPLY", "title" => __("FTP weird pass reply", "wplnst"), "desc" => __("After having sent the FTP password to the server, libcurl expects a proper reply. This error code indicates that an unexpected code was returned.", "wplnst")),
53
+ 12 => array("code" => "CURLE_FTP_ACCEPT_TIMEOUT", "title" => __("FTP accept timeout", "wplnst"), "desc" => __("During an active FTP session while waiting for the server to connect, the CURLOPT_ACCEPTTIMEOUT_MS (or the internal default) timeout expired.", "wplnst")),
54
+ 13 => array("code" => "CURLE_FTP_WEIRD_PASV_REPLY", "title" => __("FTP weird pasv reply", "wplnst"), "desc" => __("libcurl failed to get a sensible result back from the server as a response to either a PASV or a EPSV command. The server is flawed.", "wplnst")),
55
+ 14 => array("code" => "CURLE_FTP_WEIRD_227_FORMAT", "title" => __("FTP weird 227 format", "wplnst"), "desc" => __("FTP servers return a 227-line as a response to a PASV command. If libcurl fails to parse that line, this return code is passed back.", "wplnst")),
56
+ 15 => array("code" => "CURLE_FTP_CANT_GET_HOST", "title" => __("FTP can't get host", "wplnst"), "desc" => __("An internal failure to lookup the host used for the new connection.", "wplnst")),
57
+ 16 => array("code" => "CURLE_HTTP2", "title" => __("HTTP2 framing layer problem", "wplnst"), "desc" => __("A problem was detected in the HTTP2 framing layer. This is somewhat generic and can be one out of several problems, see the error buffer for details.", "wplnst")),
58
+ 17 => array("code" => "CURLE_FTP_COULDNT_SET_TYPE", "title" => __("FTP couldn't set type", "wplnst"), "desc" => __("Received an error when trying to set the transfer mode to binary or ASCII.", "wplnst")),
59
+ 18 => array("code" => "CURLE_PARTIAL_FILE", "title" => __("Partial file", "wplnst"), "desc" => __("A file transfer was shorter or larger than expected. This happens when the server first reports an expected transfer size, and then delivers data that doesn't match the previously given size.", "wplnst")),
60
+ 19 => array("code" => "CURLE_FTP_COULDNT_RETR_FILE", "title" => __("FTP couldn't retrieve file", "wplnst"), "desc" => __("This was either a weird reply to a 'RETR' command or a zero byte transfer complete.", "wplnst")),
61
+ 21 => array("code" => "CURLE_QUOTE_ERROR", "title" => __("Quote error", "wplnst"), "desc" => __("When sending custom 'QUOTE' commands to the remote server, one of the commands returned an error code that was 400 or higher (for FTP) or otherwise indicated unsuccessful completion of the command.", "wplnst")),
62
+ 22 => array("code" => "CURLE_HTTP_RETURNED_ERROR", "title" => __("HTTP returned error", "wplnst"), "desc" => __("This is returned if CURLOPT_FAILONERROR is set TRUE and the HTTP server returns an error code that is >= 400.", "wplnst")),
63
+ 23 => array("code" => "CURLE_WRITE_ERROR", "title" => __("Write error", "wplnst"), "desc" => __("An error occurred when writing received data to a local file, or an error was returned to libcurl from a write callback.", "wplnst")),
64
+ 25 => array("code" => "CURLE_UPLOAD_FAILED", "title" => __("Upload failed", "wplnst"), "desc" => __("Failed starting the upload. For FTP, the server typically denied the STOR command. The error buffer usually contains the server's explanation for this.", "wplnst")),
65
+ 26 => array("code" => "CURLE_READ_ERROR", "title" => __("Read error", "wplnst"), "desc" => __("There was a problem reading a local file or an error returned by the read callback.", "wplnst")),
66
+ 27 => array("code" => "CURLE_OUT_OF_MEMORY", "title" => __("Out of memory", "wplnst"), "desc" => __("A memory allocation request failed. This is serious badness and things are severely screwed up if this ever occurs.", "wplnst")),
67
+ 28 => array("code" => "CURLE_OPERATION_TIMEDOUT", "title" => __("Operation timedout", "wplnst"), "desc" => __("The specified time-out period was reached according to the conditions.", "wplnst")),
68
+ 30 => array("code" => "CURLE_FTP_PORT_FAILED", "title" => __("FTP port failed", "wplnst"), "desc" => __("The FTP PORT command returned error. This mostly happens when you haven't specified a good enough address for libcurl to use. See CURLOPT_FTPPORT.", "wplnst")),
69
+ 31 => array("code" => "CURLE_FTP_COULDNT_USE_REST", "title" => __("FTP couldn't use REST", "wplnst"), "desc" => __("The FTP REST command returned error. This should never happen if the server is sane.", "wplnst")),
70
+ 33 => array("code" => "CURLE_RANGE_ERROR", "title" => __("Range error", "wplnst"), "desc" => __("The server does not support or accept range requests.", "wplnst")),
71
+ 34 => array("code" => "CURLE_HTTP_POST_ERROR", "title" => __("HTTP post error", "wplnst"), "desc" => __("This is an odd error that mainly occurs due to internal confusion.", "wplnst")),
72
+ 35 => array("code" => "CURLE_SSL_CONNECT_ERROR", "title" => __("SSL connect error", "wplnst"), "desc" => __("A problem occurred somewhere in the SSL/TLS handshake. You really want the error buffer and read the message there as it pinpoints the problem slightly more. Could be certificates (file formats, paths, permissions), passwords, and others.", "wplnst")),
73
+ 36 => array("code" => "CURLE_BAD_DOWNLOAD_RESUME", "title" => __("Bad download resume", "wplnst"), "desc" => __("The download could not be resumed because the specified offset was out of the file boundary.", "wplnst")),
74
+ 37 => array("code" => "CURLE_FILE_COULDNT_READ_FILE", "title" => __("File couldn't read file", "wplnst"), "desc" => __("A file given with FILE:// couldn't be opened. Most likely because the file path doesn't identify an existing file. Did you check file permissions?", "wplnst")),
75
+ 38 => array("code" => "CURLE_LDAP_CANNOT_BIND", "title" => __("LDAP cannot bind", "wplnst"), "desc" => __("LDAP cannot bind. LDAP bind operation failed.", "wplnst")),
76
+ 39 => array("code" => "CURLE_LDAP_SEARCH_FAILED", "title" => __("LDAP search failed", "wplnst"), "desc" => __("LDAP search failed.", "wplnst")),
77
+ 41 => array("code" => "CURLE_FUNCTION_NOT_FOUND", "title" => __("Function not found", "wplnst"), "desc" => __("Function not found. A required zlib function was not found.", "wplnst")),
78
+ 42 => array("code" => "CURLE_ABORTED_BY_CALLBACK", "title" => __("Aborted by callback", "wplnst"), "desc" => __("Aborted by callback. A callback returned 'abort' to libcurl.", "wplnst")),
79
+ 43 => array("code" => "CURLE_BAD_FUNCTION_ARGUMENT", "title" => __("Bad function argument", "wplnst"), "desc" => __("Internal error. A function was called with a bad parameter.", "wplnst")),
80
+ 45 => array("code" => "CURLE_INTERFACE_FAILED", "title" => __("Interface failed", "wplnst"), "desc" => __("Interface error. A specified outgoing interface could not be used. Set which interface to use for outgoing connections source IP address with CURLOPT_INTERFACE.", "wplnst")),
81
+ 47 => array("code" => "CURLE_TOO_MANY_REDIRECTS", "title" => __("Too many redirections", "wplnst"), "desc" => __("Too many redirects. When following redirects, libcurl hit the maximum amount. Set your limit with CURLOPT_MAXREDIRS.", "wplnst")),
82
+ 48 => array("code" => "CURLE_UNKNOWN_OPTION", "title" => __("Unknown option", "wplnst"), "desc" => __("An option passed to libcurl is not recognized/known. Refer to the appropriate documentation. This is most likely a problem in the program that uses libcurl. The error buffer might contain more specific information about which exact option it concerns.", "wplnst")),
83
+ 49 => array("code" => "CURLE_TELNET_OPTION_SYNTAX", "title" => __("Telnet option syntax", "wplnst"), "desc" => __("A telnet option string was Illegally formatted.", "wplnst")),
84
+ 51 => array("code" => "CURLE_PEER_FAILED_VERIFICATION", "title" => __("Peer failed verification", "wplnst"), "desc" => __("The remote server's SSL certificate or SSH md5 fingerprint was deemed not OK.", "wplnst")),
85
+ 52 => array("code" => "CURLE_GOT_NOTHING", "title" => __("Got nothing", "wplnst"), "desc" => __("Nothing was returned from the server, and under the circumstances, getting nothing is considered an error.", "wplnst")),
86
+ 53 => array("code" => "CURLE_SSL_ENGINE_NOTFOUND", "title" => __("SSL engine not found", "wplnst"), "desc" => __("The specified crypto engine wasn't found.", "wplnst")),
87
+ 54 => array("code" => "CURLE_SSL_ENGINE_SETFAILED", "title" => __("SSL engine set failed", "wplnst"), "desc" => __("Failed setting the selected SSL crypto engine as default!", "wplnst")),
88
+ 55 => array("code" => "CURLE_SEND_ERROR", "title" => __("Send error", "wplnst"), "desc" => __("Failed sending network data.", "wplnst")),
89
+ 56 => array("code" => "CURLE_RECV_ERROR", "title" => __("Receive error", "wplnst"), "desc" => __("Failure with receiving network data.", "wplnst")),
90
+ 58 => array("code" => "CURLE_SSL_CERTPROBLEM", "title" => __("Certificate problem", "wplnst"), "desc" => __("Problem with the local client certificate. ", "wplnst")),
91
+ 59 => array("code" => "CURLE_SSL_CIPHER", "title" => __("SSL cipher", "wplnst"), "desc" => __("Couldn't use specified cipher.", "wplnst")),
92
+ 60 => array("code" => "CURLE_SSL_CACERT", "title" => __("SSL CA certificate", "wplnst"), "desc" => __("Peer certificate cannot be authenticated with known CA certificates.", "wplnst")),
93
+ 61 => array("code" => "CURLE_BAD_CONTENT_ENCODING", "title" => __("Bad content encoding", "wplnst"), "desc" => __("Unrecognized transfer encoding.", "wplnst")),
94
+ 62 => array("code" => "CURLE_LDAP_INVALID_URL", "title" => __("LDAP invalid URL", "wplnst"), "desc" => __("Invalid LDAP URL.", "wplnst")),
95
+ 63 => array("code" => "CURLE_FILESIZE_EXCEEDED", "title" => __("File size exceeded", "wplnst"), "desc" => __("Maximum file size exceeded.", "wplnst")),
96
+ 64 => array("code" => "CURLE_USE_SSL_FAILED", "title" => __("Use SSL failed", "wplnst"), "desc" => __("Requested FTP SSL level failed. ", "wplnst")),
97
+ 65 => array("code" => "CURLE_SEND_FAIL_REWIND", "title" => __("Send fail rewind", "wplnst"), "desc" => __("When doing a send operation curl had to rewind the data to retransmit, but the rewinding operation failed.", "wplnst")),
98
+ 66 => array("code" => "CURLE_SSL_ENGINE_INITFAILED", "title" => __("SSL initialization failed", "wplnst"), "desc" => __("Initiating the SSL Engine failed.", "wplnst")),
99
+ 67 => array("code" => "CURLE_LOGIN_DENIED", "title" => __("Login denied", "wplnst"), "desc" => __("The remote server denied curl to login (Added in 7.13.1)", "wplnst")),
100
+ 68 => array("code" => "CURLE_TFTP_NOTFOUND", "title" => __("TFTP file not found", "wplnst"), "desc" => __("File not found on TFTP server.", "wplnst")),
101
+ 69 => array("code" => "CURLE_TFTP_PERM", "title" => __("TFTP permission", "wplnst"), "desc" => __("Permission problem on TFTP server.", "wplnst")),
102
+ 70 => array("code" => "CURLE_REMOTE_DISK_FULL", "title" => __("Remote disk full", "wplnst"), "desc" => __("Out of disk space on the server.", "wplnst")),
103
+ 71 => array("code" => "CURLE_TFTP_ILLEGAL", "title" => __("TFTP illegal", "wplnst"), "desc" => __("Illegal TFTP operation.", "wplnst")),
104
+ 72 => array("code" => "CURLE_TFTP_UNKNOWNID", "title" => __("FTP unknown ID", "wplnst"), "desc" => __("Unknown TFTP transfer ID.", "wplnst")),
105
+ 73 => array("code" => "CURLE_REMOTE_FILE_EXISTS", "title" => __("Remote file exists", "wplnst"), "desc" => __("File already exists and will not be overwritten.", "wplnst")),
106
+ 74 => array("code" => "CURLE_TFTP_NOSUCHUSER", "title" => __("TFTP no such user", "wplnst"), "desc" => __("This error should never be returned by a properly functioning TFTP server.", "wplnst")),
107
+ 75 => array("code" => "CURLE_CONV_FAILED", "title" => __("Conversion failed", "wplnst"), "desc" => __("Character conversion failed.", "wplnst")),
108
+ 76 => array("code" => "CURLE_CONV_REQD", "title" => __("Conversion callbacks", "wplnst"), "desc" => __("Caller must register conversion callbacks.", "wplnst")),
109
+ 77 => array("code" => "CURLE_SSL_CACERT_BADFILE", "title" => __("SSL CA certificate bad file", "wplnst"), "desc" => __("Problem with reading the SSL CA cert (path? access rights?)", "wplnst")),
110
+ 78 => array("code" => "CURLE_REMOTE_FILE_NOT_FOUND", "title" => __("Remote file not found", "wplnst"), "desc" => __("The resource referenced in the URL does not exist.", "wplnst")),
111
+ 79 => array("code" => "CURLE_SSH", "title" => __("SSH error", "wplnst"), "desc" => __("An unspecified error occurred during the SSH session.", "wplnst")),
112
+ 80 => array("code" => "CURLE_SSL_SHUTDOWN_FAILED", "title" => __("SSL shutdown failed", "wplnst"), "desc" => __("Failed to shut down the SSL connection.", "wplnst")),
113
+ 81 => array("code" => "CURLE_AGAIN", "title" => __("Socket not ready", "wplnst"), "desc" => __("Socket is not ready for send/recv wait till it's ready and try again. This return code is only returned from curl_easy_recv and curl_easy_send (Added in 7.18.2)", "wplnst")),
114
+ 82 => array("code" => "CURLE_SSL_CRL_BADFILE", "title" => __("SSL CRL bad file", "wplnst"), "desc" => __("Failed to load CRL file (Added in 7.19.0)", "wplnst")),
115
+ 83 => array("code" => "CURLE_SSL_ISSUER_ERROR", "title" => __("SSL issue error", "wplnst"), "desc" => __("Issuer check failed (Added in 7.19.0)", "wplnst")),
116
+ 84 => array("code" => "CURLE_FTP_PRET_FAILED", "title" => __("FTP PRET failed", "wplnst"), "desc" => __("The FTP server does not understand the PRET command at all or does not support the given argument. Be careful when using CURLOPT_CUSTOMREQUEST, a custom LIST command will be sent with PRET CMD before PASV as well. (Added in 7.20.0)", "wplnst")),
117
+ 85 => array("code" => "CURLE_RTSP_CSEQ_ERROR", "title" => __("RTSP CSEQ error", "wplnst"), "desc" => __("Mismatch of RTSP CSeq numbers.", "wplnst")),
118
+ 86 => array("code" => "CURLE_RTSP_SESSION_ERROR", "title" => __("RTSP session error", "wplnst"), "desc" => __("Mismatch of RTSP Session Identifiers.", "wplnst")),
119
+ 87 => array("code" => "CURLE_FTP_BAD_FILE_LIST", "title" => __("FTP bad file list", "wplnst"), "desc" => __("Unable to parse FTP file list (during FTP wildcard downloading).", "wplnst")),
120
+ 88 => array("code" => "CURLE_CHUNK_FAILED", "title" => __("Chunk failed", "wplnst"), "desc" => __("Chunk callback reported error.", "wplnst")),
121
+ 89 => array("code" => "CURLE_NO_CONNECTION_AVAILABLE", "title" => __("No connection available", "wplnst"), "desc" => __("(For internal use only, will never be returned by libcurl) No connection available, the session will be queued. (added in 7.30.0)", "wplnst")),
122
+ );
123
+ }
124
+
125
+
126
+
127
+ }
core/types.php ADDED
@@ -0,0 +1,752 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * WP Link Status Core Types class
5
+ *
6
+ * @package WP Link Status
7
+ * @subpackage WP Link Status Core
8
+ */
9
+ class WPLNST_Core_Types {
10
+
11
+
12
+
13
+ // Constants
14
+ // ---------------------------------------------------------------------------------------------------
15
+
16
+
17
+
18
+ /**
19
+ * Post types avoided
20
+ */
21
+ const post_types_avoid = 'attachment, nav_menu_item, revision';
22
+
23
+
24
+
25
+ /**
26
+ * Post status allowed
27
+ */
28
+ const post_status_allow = 'publish, future, draft, pending, private, trash';
29
+
30
+
31
+
32
+ /**
33
+ * Default scans and results per page
34
+ */
35
+ const scans_per_page = 5;
36
+ const scans_results_per_page = 25;
37
+
38
+
39
+
40
+ // Data types
41
+ // ---------------------------------------------------------------------------------------------------
42
+
43
+
44
+
45
+ /**
46
+ * Post types allowed
47
+ */
48
+ public static function get_post_types($output = 'keys-names') {
49
+
50
+ // Current avoid post types
51
+ $avoid_post_types = apply_filters('wplnst_avoid_post_types', array_map('trim', explode(',', self::post_types_avoid)));
52
+ if (empty($avoid_post_types) || !is_array($avoid_post_types))
53
+ return false;
54
+
55
+ // Compute allowed post types
56
+ $post_types = get_post_types(array(), 'objects');
57
+ $allowed_post_types = array_diff_key($post_types, array_fill_keys($avoid_post_types, true));
58
+
59
+ // Return key-name values
60
+ if ('keys-names' == $output) {
61
+ $keys_names = array();
62
+ foreach ($allowed_post_types as $key => $post_type)
63
+ $keys_names[$key] = $post_type->labels->name;
64
+ return $keys_names;
65
+
66
+ // Return keys
67
+ } elseif ('keys' == $output) {
68
+ return array_keys($allowed_post_types);
69
+
70
+ // Return names
71
+ } elseif ('names' == $output) {
72
+ $names = array();
73
+ foreach ($allowed_post_types as $key => $post_type)
74
+ $names[] = $post_type->labels->name;
75
+ return $names;
76
+ }
77
+
78
+ // Default
79
+ return $allowed_post_types;
80
+ }
81
+
82
+
83
+
84
+ /**
85
+ * Post status allowed
86
+ */
87
+ public static function get_post_status() {
88
+ $post_status_allowed = array_map('trim', explode(',', self::post_status_allow));
89
+ $post_status = apply_filters('wplnst_allow_post_status', $post_status_allowed);
90
+ return (empty($post_status) && !is_array($post_status))? false : array_intersect($post_status, $post_status_allowed);
91
+ }
92
+
93
+
94
+
95
+ /**
96
+ * Return an array of tipified objects
97
+ */
98
+ public static function get_objects_types() {
99
+ return array(
100
+ 'posts' => __('Entries', 'wplnst'),
101
+ 'comments' => __('Comments', 'wplnst'),
102
+ 'blogroll' => __('Blogroll', 'wplnst'),
103
+ );
104
+ }
105
+
106
+
107
+
108
+ /**
109
+ * Return an array of possible scan status
110
+ */
111
+ public static function get_scan_statuses() {
112
+ return array(
113
+ 'wait' => __('Waiting', 'wplnst'),
114
+ 'queued' => __('Queued', 'wplnst'),
115
+ 'play' => __('Running', 'wplnst'),
116
+ 'stop' => __('Stopped', 'wplnst'),
117
+ 'end' => __('Completed', 'wplnst'),
118
+ );
119
+ }
120
+
121
+
122
+
123
+ /**
124
+ * Return link destination types
125
+ */
126
+ public static function get_destination_types() {
127
+ return array(
128
+ 'all' => __('All URLs', 'wplnst'),
129
+ 'internal' => __('Internal URLs', 'wplnst'),
130
+ 'external' => __('External URLs', 'wplnst'),
131
+ );
132
+ }
133
+
134
+
135
+
136
+ /**
137
+ * Return available time scopes
138
+ */
139
+ public static function get_time_scopes() {
140
+ return array(
141
+ 'anytime' => __('Anytime content', 'wplnst'),
142
+ 'yesterday' => __('From yesterday', 'wplnst'),
143
+ '7days' => __('Last 7 days', 'wplnst'),
144
+ '15days' => __('Last 15 days', 'wplnst'),
145
+ 'month' => __('One month', 'wplnst'),
146
+ '3months' => __('Last 3 months', 'wplnst'),
147
+ '6months' => __('Last 6 months', 'wplnst'),
148
+ 'year' => __('One year', 'wplnst'),
149
+ /* 'custom' => 'Custom' */
150
+ );
151
+ }
152
+
153
+
154
+
155
+ /**
156
+ * Return link types
157
+ */
158
+ public static function get_link_types() {
159
+ return array(
160
+ 'links' => __('Links', 'wplnst'),
161
+ 'images' => __('Images', 'wplnst'),
162
+ );
163
+ }
164
+
165
+
166
+
167
+ /**
168
+ * Return crawl order values
169
+ */
170
+ public static function get_crawl_order() {
171
+ return array(
172
+ 'desc' => __('Most recent content', 'wplnst'),
173
+ 'asc' => __('Oldest content first', 'wplnst'),
174
+ );
175
+ }
176
+
177
+
178
+
179
+ /**
180
+ * Return custom fields types array
181
+ */
182
+ public static function get_custom_fields() {
183
+ return array(
184
+ 'url' => 'URL',
185
+ 'html' => 'HTML',
186
+ );
187
+ }
188
+
189
+
190
+
191
+ /**
192
+ * Return allowed comment types
193
+ */
194
+ public static function get_comment_types() {
195
+ return array(
196
+ 'approved' => __('Approved', 'wplnst'),
197
+ 'pending' => __('Pending', 'wplnst'),
198
+ );
199
+ }
200
+
201
+
202
+
203
+ /**
204
+ * Return database values for comment types keys
205
+ */
206
+ public static function get_comment_types_values($comment_types) {
207
+ $comment_types_values = array();
208
+ $eq = array('approved' => '1', 'pending' => '0');
209
+ foreach ($comment_types as $comment_type) {
210
+ if (isset($eq[$comment_type]))
211
+ $comment_types_values[] = $eq[$comment_type];
212
+ }
213
+ return $comment_types_values;
214
+ }
215
+
216
+
217
+
218
+ /**
219
+ * Return anchor filters types array
220
+ */
221
+ public static function get_anchor_filters() {
222
+ return array(
223
+ 'contains' => __('Contains', 'wplnst'),
224
+ 'not-contains' => __('Not contains', 'wplnst'),
225
+ 'equal-to' => __('Is equal to', 'wplnst'),
226
+ 'not-equal-to' => __('Not equal to', 'wplnst'),
227
+ 'begins-with' => __('Starts with', 'wplnst'),
228
+ 'ends-by' => __('Ends by', 'wplnst'),
229
+ 'empty' => __('Empty', 'wplnst'),
230
+ );
231
+ }
232
+
233
+
234
+
235
+ /**
236
+ * Return filters for Anchor text search
237
+ */
238
+ public static function get_anchor_search_filters() {
239
+ return array(
240
+ 'm' => __('Matched string', 'wplnst'),
241
+ 'p' => __('Starts with', 'wplnst'),
242
+ 's' => __('Ends by', 'wplnst'),
243
+ 'f' => __('Full anchor', 'wplnst'),
244
+ );
245
+ }
246
+
247
+
248
+
249
+ /**
250
+ * Return filters for URL inclusion and exclusion
251
+ */
252
+ public static function get_url_filters() {
253
+ return array(
254
+ 'matched-string' => __('Matched string', 'wplnst'),
255
+ 'url-prefix' => __('URL prefix', 'wplnst'),
256
+ 'url-suffix' => __('URL suffix', 'wplnst'),
257
+ 'full-url' => __('Full URL', 'wplnst'),
258
+ );
259
+ }
260
+
261
+
262
+
263
+ /**
264
+ * Return filters for URL search
265
+ */
266
+ public static function get_url_search_filters() {
267
+ return array(
268
+ 'm' => __('Matched string', 'wplnst'),
269
+ 'p' => __('URL prefix', 'wplnst'),
270
+ 's' => __('URL suffix', 'wplnst'),
271
+ 'r' => __('URL fragment #', 'wplnst'),
272
+ 'f' => __('Full URL', 'wplnst'),
273
+ );
274
+ }
275
+
276
+
277
+
278
+ /**
279
+ * Return array of HTML elements having options
280
+ */
281
+ public static function get_html_attributes_having() {
282
+ return array(
283
+ 'have' => __('Has', 'wplnst'),
284
+ 'not-have' => __('Not have', 'wplnst'),
285
+ );
286
+ }
287
+
288
+
289
+
290
+ /**
291
+ * Return array of HTML elements operation
292
+ */
293
+ public static function get_html_attributes_operators() {
294
+ return array(
295
+ 'contains' => __('Contains', 'wplnst'),
296
+ 'not-contains' => __('Not contains', 'wplnst'),
297
+ 'equal' => __('Is equal to', 'wplnst'),
298
+ 'not-equal' => __('Not equal to', 'wplnst'),
299
+ 'not-empty' => __('Not empty', 'wplnst'),
300
+ 'empty' => __('Empty', 'wplnst'),
301
+ );
302
+ }
303
+
304
+
305
+
306
+ /**
307
+ * All status levels
308
+ */
309
+ public static function get_status_levels() {
310
+ return array(
311
+ '2' => __('Success', 'wplnst'),
312
+ '3' => __('Redirections', 'wplnst'),
313
+ '4' => __('Errors', 'wplnst'),
314
+ '5' => __('Server Error', 'wplnst'),
315
+ );
316
+ }
317
+
318
+
319
+
320
+ /**
321
+ * All status codes
322
+ */
323
+ public static function get_status_codes() {
324
+ return array(
325
+ '2' => array(
326
+ '200' => __('OK', 'wplnst'),
327
+ '201' => __('Created', 'wplnst'),
328
+ '202' => __('Accepted', 'wplnst'),
329
+ '203' => __('Non-Authoritative Information', 'wplnst'),
330
+ '204' => __('No Content', 'wplnst'),
331
+ '205' => __('Reset Content', 'wplnst'),
332
+ '206' => __('Partial Content', 'wplnst'),
333
+ ),
334
+ '3' => array(
335
+ '300' => __('Multiple Choices', 'wplnst'),
336
+ '301' => __('Moved Permanently', 'wplnst'),
337
+ '302' => __('Found', 'wplnst'),
338
+ '303' => __('See Other', 'wplnst'),
339
+ '304' => __('Not Modified', 'wplnst'),
340
+ '305' => __('Use Proxy', 'wplnst'),
341
+ '307' => __('Temporary Redirect', 'wplnst'),
342
+ ),
343
+ '4' => array(
344
+ '400' => __('Bad Request', 'wplnst'),
345
+ '401' => __('Unauthorized', 'wplnst'),
346
+ '402' => __('Payment Required', 'wplnst'),
347
+ '403' => __('Forbidden', 'wplnst'),
348
+ '404' => __('Not Found', 'wplnst'),
349
+ '405' => __('Method Not Allowed', 'wplnst'),
350
+ '406' => __('Not Acceptable', 'wplnst'),
351
+ '407' => __('Proxy Authentication Required', 'wplnst'),
352
+ '408' => __('Request Timeout', 'wplnst'),
353
+ '409' => __('Conflict', 'wplnst'),
354
+ '410' => __('Gone', 'wplnst'),
355
+ '411' => __('Length Required', 'wplnst'),
356
+ '412' => __('Precondition Failed', 'wplnst'),
357
+ '413' => __('Request Entity Too Large', 'wplnst'),
358
+ '414' => __('Request-URI Too Long', 'wplnst'),
359
+ '415' => __('Unsupported Media Type', 'wplnst'),
360
+ '416' => __('Requested Range Not Satisfiable', 'wplnst'),
361
+ '417' => __('Expectation Failed', 'wplnst'),
362
+ ),
363
+ '5' => array(
364
+ '500' => __('Internal Server Error', 'wplnst'),
365
+ '501' => __('Not Implemented', 'wplnst'),
366
+ '502' => __('Bad Gateway', 'wplnst'),
367
+ '503' => __('Service Unavailable', 'wplnst'),
368
+ '504' => __('Gateway Timeout', 'wplnst'),
369
+ '505' => __('HTTP Version Not Supported', 'wplnst'),
370
+ )
371
+ );
372
+ }
373
+
374
+
375
+
376
+ /**
377
+ * Return only codes in one-dimensional array
378
+ */
379
+ public static function get_status_codes_raw() {
380
+ $raw = array();
381
+ $codes = self::get_status_codes();
382
+ foreach ($codes as $level => $status) {
383
+ foreach ($status as $code => $description)
384
+ $raw[$code] = $description;
385
+ }
386
+ return $raw;
387
+ }
388
+
389
+
390
+
391
+ /**
392
+ * Return SEO link types
393
+ */
394
+ public static function get_seo_link_types() {
395
+ return array(
396
+ 'nf' => __('NoFollow', 'wplnst'),
397
+ 'df' => __('DoFollow', 'wplnst'),
398
+ );
399
+ }
400
+
401
+
402
+
403
+ /**
404
+ * Return protocol filters
405
+ */
406
+ public static function get_protocol_types() {
407
+ return array(
408
+ 'http' => 'HTTP',
409
+ 'https' => 'HTTPS',
410
+ 'rel' => 'Relative //',
411
+ );
412
+ }
413
+
414
+
415
+
416
+ /**
417
+ * Return special types of URLs
418
+ */
419
+ public static function get_special_types() {
420
+ return array(
421
+ 'rel' => __('Relative', 'wplnst'),
422
+ 'abs' => __('Absolute', 'wplnst'),
423
+ 'spa' => __('Spaced', 'wplnst'),
424
+ 'mal' => __('Malformed', 'wplnst'),
425
+ );
426
+ }
427
+
428
+
429
+
430
+ /**
431
+ * Return actions performed
432
+ */
433
+ public static function get_action_types() {
434
+ return array(
435
+ 'unl' => __('Unlinked', 'wplnst'),
436
+ 'mod' => __('Modified', 'wplnst'),
437
+ 'umd' => __('Unmodified', 'wplnst'),
438
+ 'rec' => __('Rechecked', 'wplnst'),
439
+ );
440
+ }
441
+
442
+
443
+
444
+ /**
445
+ * Return filter for ignored results
446
+ */
447
+ public static function get_ignored_types() {
448
+ return array(
449
+ 'oir' => __('Only ignored results', 'wplnst'),
450
+ 'ian' => __('Ignored and not ignored', 'wplnst'),
451
+ );
452
+ }
453
+
454
+
455
+
456
+ /**
457
+ * Return allowed order by
458
+ */
459
+ public static function get_order_types() {
460
+ return array(
461
+ 'dma' => __('Domain name ASC', 'wplnst'),
462
+ 'dmd' => __('Domain name DESC', 'wplnst'),
463
+ 'dta' => __('Download time ASC', 'wplnst'),
464
+ 'dtd' => __('Download time DESC', 'wplnst'),
465
+ 'dsa' => __('Download size ASC', 'wplnst'),
466
+ 'dsd' => __('Download size DESC', 'wplnst'),
467
+ );
468
+ }
469
+
470
+
471
+
472
+ // Selected types name
473
+ // ---------------------------------------------------------------------------------------------------
474
+
475
+
476
+
477
+ /**
478
+ * Resolve Destination Type name from value
479
+ */
480
+ public static function get_destination_type_name($value, $default = false) {
481
+ return self::get_field_value_name(self::get_destination_types(), $value, $default);
482
+ }
483
+
484
+
485
+
486
+ /**
487
+ * Resolve Time Scope name from value
488
+ */
489
+ public static function get_time_scope_name($value, $default = false) {
490
+ return self::get_field_value_name(self::get_time_scopes(), $value, $default);
491
+ }
492
+
493
+
494
+
495
+ /**
496
+ * Resolve Links Types from values
497
+ */
498
+ public static function get_link_types_names($value, $default = false) {
499
+ return self::get_field_values_names(self::get_link_types(), $value, $default);
500
+ }
501
+
502
+
503
+
504
+ /**
505
+ * Resolve Crawl Order name from value
506
+ */
507
+ public static function get_crawl_order_name($value, $default = false) {
508
+ return self::get_field_value_name(self::get_crawl_order(), $value, $default);
509
+ }
510
+
511
+
512
+
513
+ /**
514
+ * Resolve post types from values
515
+ */
516
+ public static function get_post_types_names($value, $default = false) {
517
+ return self::get_field_values_names(self::get_post_types(), $value, $default);
518
+ }
519
+
520
+
521
+
522
+ /**
523
+ * Resolve post status from values
524
+ */
525
+ public static function get_post_status_names($value, $default = false) {
526
+ return self::get_field_values_names(self::get_post_status(), $value, $default);
527
+ }
528
+
529
+
530
+
531
+ /**
532
+ * Resolve Comments Types from values
533
+ */
534
+ public static function get_comment_types_names($value, $default = false) {
535
+ return self::get_field_values_names(self::get_comment_types(), $value, $default);
536
+ }
537
+
538
+
539
+
540
+ /**
541
+ * Resolve links level status
542
+ */
543
+ public static function get_links_status_levels_names($value, $default = false) {
544
+ return self::get_field_values_names(self::get_status_levels(), $value, $default);
545
+ }
546
+
547
+
548
+
549
+ /**
550
+ * Resolve links codes status
551
+ */
552
+ public static function get_links_status_codes_names($value, $default = false) {
553
+ return self::get_field_values_names(self::get_status_codes_raw(), $value, $default);
554
+ }
555
+
556
+
557
+
558
+ /**
559
+ * Resolve names, combined with levels and codes
560
+ */
561
+ public static function get_links_status_names_combined($status_levels_values, $status_codes_values) {
562
+
563
+ // Initialize
564
+ $names = array();
565
+
566
+ // Resolve levels
567
+ $status_codes = self::get_status_codes();
568
+ $status_levels = self::get_status_levels();
569
+ foreach ($status_levels as $key => $level_name) {
570
+ if (in_array($key, $status_levels_values))
571
+ $names[] = $key.'00s '.$level_name;
572
+ if (isset($status_codes[$key])) {
573
+ foreach ($status_codes[$key] as $code => $code_name) {
574
+ if (in_array($code, $status_codes_values))
575
+ $names[] = $code.' '.$code_name;
576
+ }
577
+ }
578
+ }
579
+
580
+ // Donde
581
+ return $names;
582
+ }
583
+
584
+
585
+
586
+ /**
587
+ * Resolve a name from key value
588
+ */
589
+ public static function get_field_value_name($array, $value, $default = false) {
590
+
591
+ // Check name value
592
+ if (isset($array[$value]))
593
+ return $array[$value];
594
+
595
+ // Check default value
596
+ if (false !== $default && isset($array[$default]))
597
+ return $array[$default];
598
+
599
+ // Nothing
600
+ return '';
601
+ }
602
+
603
+
604
+
605
+ /**
606
+ * Resolve an array of names from an array of key values
607
+ */
608
+ public static function get_field_values_names($array, $values, $default = false) {
609
+
610
+ // Initialize
611
+ $names = array();
612
+
613
+ // Enum values
614
+ foreach ($values as $key) {
615
+ if (isset($array[$key]))
616
+ $names[] = $array[$key];
617
+ }
618
+
619
+ // Check default for empty names
620
+ if (empty($names) && false !== $default) {
621
+ if (!is_array($default)) {
622
+ if (isset($array[$default]))
623
+ $names[] = $array[$default];
624
+ } else {
625
+ foreach ($default as $key) {
626
+ if (isset($array[$key]))
627
+ $names[] = $array[$key];
628
+ }
629
+ }
630
+ }
631
+
632
+ // Done
633
+ return $names;
634
+ }
635
+
636
+
637
+
638
+ // Validation
639
+ // ---------------------------------------------------------------------------------------------------
640
+
641
+
642
+
643
+ /**
644
+ * Check submit POST value
645
+ */
646
+ public static function check_post_value($param, $allowed, $default = null) {
647
+ return (!empty($_POST) && is_array($_POST) && isset($_POST[$param]))? self::check_allowed_value($_POST[$param], $allowed, $default) : $default;
648
+ }
649
+
650
+
651
+
652
+ /**
653
+ * Check submitted elist and rearrange indexes
654
+ */
655
+ public static function check_post_elist($param, $default = array()) {
656
+
657
+ // Decode value
658
+ $elist = isset($_POST[$param])? @json_decode(stripslashes($_POST[$param]), true) : false;
659
+
660
+ // Check decoding
661
+ if (empty($elist) || !is_array($elist))
662
+ return $default;
663
+
664
+ // Arrange index
665
+ $index = -1;
666
+ foreach ($elist as &$value) {
667
+ if (is_array($value))
668
+ $value['index'] = ++$index;
669
+ }
670
+
671
+ // Done
672
+ return @json_encode($elist);
673
+ }
674
+
675
+
676
+
677
+ /**
678
+ * Safe retrieve array value
679
+ */
680
+ public static function get_array_value($array, $param, $default = null) {
681
+ return (!empty($array) && is_array($array) && isset($array[$param]))? $array[$param] : $default;
682
+ }
683
+
684
+
685
+
686
+ /**
687
+ * Check array value
688
+ */
689
+ public static function check_array_value($array, $param, $allowed, $default = null) {
690
+ return (!empty($array) && is_array($array) && isset($array[$param]))? self::check_allowed_value($array[$param], $allowed, $default) : $default;
691
+ }
692
+
693
+
694
+
695
+ /**
696
+ * Check numeric array value
697
+ */
698
+ public static function check_array_numeric_value($array, $param, $default = 0) {
699
+ return empty($array[$param])? $default : (int) $array[$param];
700
+ }
701
+
702
+
703
+
704
+ /**
705
+ * Check a json array value
706
+ */
707
+ public static function check_array_json($array, $param, $default = array()) {
708
+ $value = (!empty($array) && is_array($array) && isset($array[$param]))? @json_decode($array[$param], true) : array();
709
+ return (empty($value) || !is_array($value))? $default : $value;
710
+ }
711
+
712
+
713
+
714
+ /**
715
+ * Check a value in array
716
+ */
717
+ public static function check_allowed_value($test, $allowed, $default = null) {
718
+
719
+ // Is an array
720
+ if (is_array($allowed)) {
721
+
722
+ // Initialize
723
+ $value = $default;
724
+
725
+ // Source value as an array
726
+ if (is_array($test)) {
727
+ $value = array();
728
+ foreach ($test as $key => $name) {
729
+ $sel = ('on' == $name)? $key : $name;
730
+ if (in_array($sel, $allowed))
731
+ $value[] = $sel;
732
+ }
733
+
734
+ // Single value in allowed array
735
+ } elseif (in_array($test, $allowed)) {
736
+ $value = $test;
737
+ }
738
+
739
+ // Result value
740
+ } else {
741
+
742
+ // Comparison value
743
+ $value = ($test === $allowed);
744
+ }
745
+
746
+ // Done
747
+ return $value;
748
+ }
749
+
750
+
751
+
752
+ }
core/url.php ADDED
@@ -0,0 +1,364 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * WP Link Status Core URL class
5
+ *
6
+ * @package WP Link Status
7
+ * @subpackage WP Link Status Core
8
+ */
9
+ class WPLNST_Core_URL {
10
+
11
+
12
+
13
+ // Properties
14
+ // ---------------------------------------------------------------------------------------------------
15
+
16
+
17
+
18
+ /**
19
+ * Current Home URL
20
+ */
21
+ public $home_url;
22
+
23
+
24
+
25
+ /**
26
+ * Current site host and host URL
27
+ */
28
+ public $host;
29
+ public $host_url;
30
+
31
+
32
+
33
+ // Initialization
34
+ // ---------------------------------------------------------------------------------------------------
35
+
36
+
37
+
38
+ /**
39
+ * Constructor
40
+ */
41
+ public function __construct() {
42
+
43
+ // Blog home URL
44
+ $this->home_url = rtrim(home_url(), '/');
45
+
46
+ // Paranoid mode
47
+ if (false !== ($pos = strpos($this->home_url, '#')))
48
+ $this->home_url = mb_substr($this->home_url, 0, $pos);
49
+
50
+ // Obtains host URL
51
+ $home_parts = @parse_url($this->home_url);
52
+ if (false !== $home_parts && isset($home_parts['host']) && '' !== $home_parts['host']) {
53
+
54
+ // Site host
55
+ $this->host = $home_parts['host'];
56
+
57
+ // Check scheme
58
+ $scheme = 'http';
59
+ if (!empty($home_parts['scheme']) && in_array(strtolower($home_parts['scheme']), array('http', 'https')))
60
+ $scheme = $home_parts['scheme'];
61
+
62
+ // Define host URL
63
+ $this->host_url = $scheme.'://'.$this->host;
64
+ }
65
+ }
66
+
67
+
68
+
69
+ // URL utilities
70
+ // ---------------------------------------------------------------------------------------------------
71
+
72
+
73
+
74
+ /**
75
+ * URL analysis and fragmentation
76
+ */
77
+ public function parse($value, $parent_url = false) {
78
+
79
+ // Initialize
80
+ $raw = $value;
81
+ $url = trim($raw);
82
+ $fragment = '';
83
+ $absolute = false;
84
+ $relative = false;
85
+ $protorel = false;
86
+ $malformed = false;
87
+
88
+ // Extract fragment
89
+ $test_url = $url;
90
+ if (false !== ($pos = strpos($test_url, '#'))) {
91
+ $url = mb_substr($test_url, 0, $pos);
92
+ $fragment = mb_substr($test_url, $pos);
93
+ }
94
+
95
+ // Check if begins with double slash
96
+ if ('//' == mb_substr($url, 0, 2)) {
97
+
98
+ // Initialize
99
+ $protorel = true;
100
+ $malformed = true;
101
+
102
+ // Set full URL
103
+ $full_url = ((0 === stripos($this->home_url, 'https'))? 'https' : 'http').':'.$url;
104
+
105
+ // Check scheme and host
106
+ if (false !== ($parts = @parse_url($full_url)) && !empty($parts['scheme']) && !empty($parts['host'])) {
107
+
108
+ // No malformed
109
+ $malformed = false;
110
+
111
+ // Remove possible fragment
112
+ $parts['fragment'] = null;
113
+
114
+ // Final URL
115
+ $url = $this->unparse_url($parts);
116
+ }
117
+
118
+ // Check if begins with a single slash (absolute relative)
119
+ } elseif ('/' == mb_substr($url, 0, 1)) {
120
+
121
+ // By default
122
+ $malformed = true;
123
+
124
+ // Execute parent URL filter
125
+ $parent_url = apply_filters('wplnst_relative_parent_url', $parent_url);
126
+
127
+ // Check parent
128
+ if (false !== $parent_url && !is_array($parent_url)) {
129
+
130
+ // Check parent URL
131
+ $parent_parts = @parse_url($parent_url);
132
+ if (false !== $parent_parts && isset($parent_parts['host']) && '' !== $parent_parts['host']) {
133
+
134
+ // Check scheme
135
+ $scheme = 'http';
136
+ if (!empty($parent_parts['scheme']) && in_array(strtolower($parent_parts['scheme']), array('http', 'https')))
137
+ $scheme = $parent_parts['scheme'];
138
+
139
+ // Compose full URL
140
+ $url = $scheme.'://'.trim($parent_parts['host'], '/').'/'.ltrim($url, '/');
141
+
142
+ // No malfomed
143
+ $malformed = false;
144
+
145
+ // Is absolute
146
+ $absolute = true;
147
+
148
+ // Extract parts again
149
+ $parts = @parse_url($url);
150
+ }
151
+
152
+ // This site
153
+ } else {
154
+
155
+ // Set full URL
156
+ $home_url = isset($this->host_url)? $this->host_url : $this->home_url;
157
+ $full_url = rtrim($home_url, '/').$url;
158
+
159
+ // Check scheme and host
160
+ if (false !== ($parts = @parse_url($full_url)) && !empty($parts['scheme']) && !empty($parts['host'])) {
161
+
162
+ // No malfomed
163
+ $malformed = false;
164
+
165
+ // Is absolute
166
+ $absolute = true;
167
+
168
+ // Remove possible fragment
169
+ $parts['fragment'] = null;
170
+
171
+ // Final URL
172
+ $url = $this->unparse_url($parts);
173
+ }
174
+ }
175
+
176
+ // Other
177
+ } else {
178
+
179
+ // Split parts
180
+ $parts = @parse_url($url);
181
+ if (false === $parts) {
182
+
183
+ // Malformed URL
184
+ $malformed = true;
185
+
186
+ // First relative check
187
+ } elseif (empty($parts['scheme'])) {
188
+
189
+ // Relative URL
190
+ $relative = true;
191
+
192
+ // Execute parent URL filter
193
+ $parent_url = apply_filters('wplnst_relative_parent_url', $parent_url);
194
+
195
+ // Try to compose
196
+ if (false !== $parent_url && !is_array($parent_url)) {
197
+
198
+ // Absolutize
199
+ $url = $this->absolutize($url, $parent_url);
200
+
201
+ // Extract parts again
202
+ $parts = @parse_url($url);
203
+
204
+ // This host
205
+ } else {
206
+
207
+ // Absolutize
208
+ $url = $this->absolutize($url, $this->home_url);
209
+
210
+ // Extract parts again
211
+ $parts = @parse_url($url);
212
+ }
213
+ }
214
+ }
215
+
216
+ // Check scheme, host, path and query
217
+ $scheme = isset($parts['scheme'])? $parts['scheme'] : null;
218
+ $host = isset($parts['host'])? $parts['host'] : null;
219
+ $path = isset($parts['path'])? $parts['path'] : null;
220
+ $query = isset($parts['query'])? $parts['query'] : null;
221
+
222
+ // Check link scope
223
+ $scope = (isset($host) && isset($scheme))? $this->get_scope($host, $scheme) : null;
224
+
225
+ // Check link fragment
226
+ $fragment = isset($fragment)? $fragment : (isset($parts['fragment'])? $parts['fragment'] : null);
227
+
228
+ // Done
229
+ return array(
230
+ 'url' => $url,
231
+ 'raw' => $raw,
232
+ 'scheme' => $scheme,
233
+ 'host' => $host,
234
+ 'path' => $path,
235
+ 'query' => $query,
236
+ 'fragment' => $fragment,
237
+ 'scope' => $scope,
238
+ 'spaced' => (trim($raw) != $raw),
239
+ 'malformed' => $malformed,
240
+ 'absolute' => $absolute,
241
+ 'protorel' => $protorel,
242
+ 'relative' => $relative,
243
+ 'nofollow' => false,
244
+ );
245
+ }
246
+
247
+
248
+
249
+ /**
250
+ * Unparse a parsed URL
251
+ * http://php.net/manual/es/function.parse-url.php#106731
252
+ */
253
+ public function unparse_url($parsed_url) {
254
+ $scheme = isset($parsed_url['scheme'])? $parsed_url['scheme'].'://' : '';
255
+ $host = isset($parsed_url['host'])? $parsed_url['host'] : '';
256
+ $port = isset($parsed_url['port'])? ':'.$parsed_url['port'] : '';
257
+ $user = isset($parsed_url['user'])? $parsed_url['user'] : '';
258
+ $pass = isset($parsed_url['pass'])? ':'.$parsed_url['pass'] : '';
259
+ $pass = ($user || $pass)? "$pass@" : '';
260
+ $path = isset($parsed_url['path'])? $parsed_url['path'] : '';
261
+ $query = isset($parsed_url['query'])? '?'.$parsed_url['query'] : '';
262
+ $fragment = (isset($parsed_url['fragment']) && '' != $parsed_url['fragment'])? '#'.ltrim($parsed_url['fragment'], '#') : '';
263
+ return "$scheme$user$pass$host$port$path$query$fragment";
264
+ }
265
+
266
+
267
+
268
+ /**
269
+ * Absolutize relative URL from a given permalink
270
+ */
271
+ public function absolutize($relative_url, $permalink) {
272
+
273
+ // Parse permalink
274
+ $permalink_parts = @parse_url($permalink);
275
+ if (false === $permalink_parts)
276
+ return false;
277
+
278
+ // Relative with arguments
279
+ if ('?' == mb_substr($relative_url, 0, 1)) {
280
+
281
+ // Remove permalink fragment
282
+ if (($pos = strpos($permalink, '#')) > 0)
283
+ $permalink = mb_substr($permalink, 0, $pos);
284
+
285
+ // Concatenation
286
+ return $permalink.$relative_url;
287
+
288
+ // Explode
289
+ } else {
290
+
291
+ // Initialize
292
+ $path = array();
293
+ $base = empty($permalink_parts['path'])? '/' : $permalink_parts['path'];
294
+
295
+ // Check last file
296
+ if ('/' != mb_substr($base, -1))
297
+ $base = dirname($base);
298
+
299
+ // URL parts
300
+ $parts = explode('/', $relative_url);
301
+ foreach ($parts as $part) {
302
+ if (empty($path) && '..' == $part) {
303
+ $base = dirname($base);
304
+ } elseif ('' !== $part && '.' != $part) {
305
+ $path[] = $part;
306
+ }
307
+ }
308
+
309
+ // Compose new path
310
+ $permalink_parts['path'] = rtrim($base, '/').'/'.implode('/', $path).(('/' == mb_substr($relative_url, -1))? '/' : '');
311
+
312
+ // Remove permalink parts fragment
313
+ $permalink_parts['fragment'] = null;
314
+
315
+ // Done
316
+ return $this->unparse_url($permalink_parts);
317
+ }
318
+ }
319
+
320
+
321
+
322
+ /**
323
+ * Extract attributes, original RegExp: (\S+)=["']?((?:.(?!["']?\s+(?:\S+)=|[>"']))+.)["']?
324
+ * http://stackoverflow.com/questions/317053/regular-expression-for-extracting-tag-attributes
325
+ */
326
+ public function extract_attributes($tag) {
327
+ $attr = array();
328
+ if (preg_match_all('/(\S+)=["'."'".']?((?:.(?!["'."'".']?\s+(?:\S+)=|[>"'."'".']))+.)["'."'".']?/', $tag, $matches, PREG_SET_ORDER) > 0) {
329
+ foreach ($matches as $match)
330
+ $attr[strtolower($match[1])] = $match[2];
331
+ }
332
+ return $attr;
333
+ }
334
+
335
+
336
+
337
+ /**
338
+ * Check if URL is allowed for request
339
+ */
340
+ public function is_crawleable($urlinfo) {
341
+ return ('' !== $urlinfo['url'] && false !== $urlinfo['url'] && !$urlinfo['malformed'] && isset($urlinfo['scheme']) && in_array($urlinfo['scheme'], array('http', 'https')) && isset($urlinfo['host']));
342
+ }
343
+
344
+
345
+
346
+ /**
347
+ * Check host and scheme and compare from blog host
348
+ */
349
+ public function get_scope($host, $scheme) {
350
+
351
+ // Check scheme
352
+ if (isset($this->host) && in_array(strtolower($scheme), array('http', 'https', 'ftp'))) {
353
+ $test_host = (0 === stripos($host, 'www.'))? mb_substr($host, 4) : $host;
354
+ $test_this_host = (0 === stripos($this->host, 'www.'))? mb_substr($this->host, 4) : $this->host;
355
+ return (strtolower($test_host) == strtolower($test_this_host))? 'internal' : 'external';
356
+ }
357
+
358
+ // No valid
359
+ return null;
360
+ }
361
+
362
+
363
+
364
+ }
core/util-math.php ADDED
@@ -0,0 +1,36 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * WP Link Status Core Util Math functions
5
+ *
6
+ * @package WP Link Status
7
+ * @subpackage WP Link Status Core
8
+ */
9
+
10
+
11
+
12
+ /**
13
+ * Format size from bytes to KB, MB, GB or TB
14
+ */
15
+ function wplnst_format_bytes($bytes, $precision = 3, $number_format = true) {
16
+
17
+ $units = array('B', 'KB', 'MB', 'GB', 'TB');
18
+
19
+ $bytes = max($bytes, 0);
20
+ $pow = floor(($bytes? log($bytes) : 0) / log(1024));
21
+ $pow = min($pow, count($units) - 1);
22
+
23
+ // Uncomment one of the following alternatives
24
+ $bytes /= pow(1024, $pow);
25
+ if (0 == $pow) {
26
+ $pow = 1;
27
+ if ($bytes > 0)
28
+ $bytes /= 1024;
29
+ }
30
+
31
+ $value = round($bytes, $precision);
32
+ if ($number_format && function_exists('number_format_i18n'))
33
+ $value = number_format_i18n($value, $precision);
34
+
35
+ return $value . ' ' . $units[$pow];
36
+ }
core/util-string.php ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * WP Link Status Core Util String functions
5
+ *
6
+ * @package WP Link Status
7
+ * @subpackage WP Link Status Core
8
+ */
9
+
10
+
11
+
12
+ /**
13
+ * Text cropping
14
+ */
15
+ function wplnst_crop_text($text, $chars, $dots = '...') {
16
+
17
+ // Remove HTML tags
18
+ $text = strip_tags(preg_replace('/<script\b[^>]*>(.*?)<\/script>/is', '', $text));
19
+
20
+ // All text
21
+ $text = trim($text);
22
+ if (strlen($text) <= $chars)
23
+ return $text;
24
+
25
+ // Crop text
26
+ $text = substr($text, 0, $chars);
27
+
28
+ // Remove punctuation
29
+ $text = rtrim($text, '.');
30
+ $text = rtrim($text, ',');
31
+ $text = rtrim($text, ';');
32
+ $text = rtrim($text, ':');
33
+
34
+ // Check last char
35
+ if (mb_substr($text, -1) != ' ') {
36
+ $pos = strrpos($text, ' ');
37
+ if ($pos > 0)
38
+ $text = substr($text, 0, $pos);
39
+ }
40
+
41
+ // Last chars
42
+ $text = rtrim($text, '.');
43
+ $text = rtrim($text, ',');
44
+ $text = rtrim($text, ';');
45
+ $text = rtrim($text, ':');
46
+
47
+ // Done
48
+ return $text.$dots;
49
+ }
core/util.php ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * WP Link Status Core Util functions
5
+ *
6
+ * @package WP Link Status
7
+ * @subpackage WP Link Status Core
8
+ */
9
+
10
+
11
+
12
+ // Load dependencies
13
+ require_once(dirname(__FILE__).'/settings.php');
14
+
15
+
16
+
17
+ /**
18
+ * Requires a plugin file
19
+ */
20
+ function wplnst_require($section, $filename) {
21
+ require_once(WPLNST_PATH.'/'.$section.'/'.$filename.'.php');
22
+ }
23
+
24
+
25
+
26
+ /**
27
+ * Requires multiple files for the same section
28
+ */
29
+ function wplnst_require_section($section, $filenames) {
30
+ foreach ($filenames as $filename)
31
+ wplnst_require($section, $filename);
32
+ }
33
+
34
+
35
+
36
+ /**
37
+ * Return a numeric setting
38
+ */
39
+ function wplnst_get_nsetting($name, $value = 0) {
40
+ return WPLNST_Core_Settings::get_nsetting($name, $value);
41
+ }
42
+
43
+
44
+
45
+ /**
46
+ * Return a boolean setting
47
+ */
48
+ function wplnst_get_bsetting($name, $default = false) {
49
+ return WPLNST_Core_Settings::get_bsetting($name, $default);
50
+ }
51
+
52
+
53
+
54
+ /**
55
+ * Return a text setting
56
+ */
57
+ function wplnst_get_tsetting($name, $default = true) {
58
+ return WPLNST_Core_Settings::get_tsetting($name, $default);
59
+ }
60
+
61
+
62
+
63
+ /**
64
+ * Check if cURL is enabled in this system
65
+ */
66
+ function wplnst_is_curl_enabled() {
67
+
68
+ // Last status
69
+ static $is_enabled;
70
+ if (isset($is_enabled))
71
+ return $is_enabled;
72
+
73
+ // Simple check, but it may have been overwritten
74
+ if (!function_exists('curl_version')) {
75
+ $is_enabled = false;
76
+ return false;
77
+ }
78
+
79
+ // Check extension
80
+ $extensions = @get_loaded_extensions();
81
+ if (!empty($extensions) && is_array($extensions) && in_array('curl', $extensions)) {
82
+ $is_enabled = true;
83
+ return true;
84
+ }
85
+
86
+ // Not found
87
+ $is_enabled = false;
88
+ return false;
89
+ }
90
+
91
+
92
+
93
+ /**
94
+ * Check debug flag
95
+ */
96
+ function wplnst_is_debug() {
97
+ return (defined('WPLNST_DEBUG') && WPLNST_DEBUG);
98
+ }
99
+
100
+
101
+
102
+ /**
103
+ * Output plugin debug
104
+ */
105
+ function wplnst_debug($message, $tag = '') {
106
+
107
+ // Check debug
108
+ if (!wplnst_is_debug())
109
+ return;
110
+
111
+ // Default output
112
+ if (!defined('WPLNST_DEBUG_OUTPUT') || 'error_log' == WPLNST_DEBUG_OUTPUT)
113
+ error_log('WPLNST'.(empty($tag)? '' : ' ['.$tag.']').' - '.$message);
114
+ }
languages/wp-link-status.pot ADDED
@@ -0,0 +1,2821 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ msgid ""
2
+ msgstr ""
3
+ "Project-Id-Version: WP Link Status\n"
4
+ "POT-Creation-Date: 2016-01-21 10:36+0100\n"
5
+ "PO-Revision-Date: 2016-01-21 10:36+0100\n"
6
+ "Last-Translator: Pau Iglesias <pauiglesias@gmail.com>\n"
7
+ "Language-Team: \n"
8
+ "Language: en_US\n"
9
+ "MIME-Version: 1.0\n"
10
+ "Content-Type: text/plain; charset=UTF-8\n"
11
+ "Content-Transfer-Encoding: 8bit\n"
12
+ "X-Generator: Poedit 1.5.4\n"
13
+ "X-Poedit-KeywordsList: __;_e;__ngettext;_n;__ngettext_noop;_n_noop;_x;_nx;"
14
+ "_nx_noop;_ex;esc_attr__;esc_attr_e;esc_attr_x;esc_html__;esc_html_e;"
15
+ "esc_html_x\n"
16
+ "X-Poedit-Basepath: .\n"
17
+ "X-Poedit-SourceCharset: UTF-8\n"
18
+ "Plural-Forms: nplurals=2; plural=n != 1;\n"
19
+ "X-Poedit-SearchPath-0: ..\n"
20
+
21
+ #: ../admin-pro/scans.php:51
22
+ #, php-format
23
+ msgid "Processing <b>%s</b>"
24
+ msgstr ""
25
+
26
+ #: ../admin-pro/scans.php:59 ../admin-pro/scans.php:67
27
+ msgid "Close"
28
+ msgstr ""
29
+
30
+ #: ../admin-pro/scans.php:76
31
+ msgid "Update URL"
32
+ msgstr ""
33
+
34
+ #: ../admin-pro/scans.php:77 ../admin-pro/scans.php:88
35
+ #: ../admin-pro/scans.php:99 ../admin-pro/scans.php:110
36
+ #: ../admin-pro/scans.php:121 ../admin-pro/scans.php:132
37
+ #: ../admin-pro/scans.php:143 ../admin-pro/scans.php:154
38
+ #: ../admin-pro/scans.php:165 ../admin-pro/scans.php:176
39
+ #: ../admin-pro/scans.php:187 ../admin-pro/scans.php:198
40
+ #: ../admin-pro/scans.php:209 ../admin-pro/scans.php:220
41
+ #: ../admin-pro/scans.php:231 ../views-pro/tools-url.php:21
42
+ msgid "Cancel"
43
+ msgstr ""
44
+
45
+ #: ../admin-pro/scans.php:85
46
+ msgid "Confirm <b>Unlink this URL</b>"
47
+ msgstr ""
48
+
49
+ #: ../admin-pro/scans.php:87 ../admin-pro/scans.php:98
50
+ #: ../admin-pro/scans.php:282 ../views-pro/scans-results.php:42
51
+ msgid "Unlink"
52
+ msgstr ""
53
+
54
+ #: ../admin-pro/scans.php:96
55
+ msgid "Confirm <b>Unlink selected URLs</b>"
56
+ msgstr ""
57
+
58
+ #: ../admin-pro/scans.php:107
59
+ msgid "Confirm <b>Ignore this link</b>"
60
+ msgstr ""
61
+
62
+ #: ../admin-pro/scans.php:109 ../admin-pro/scans.php:120
63
+ #: ../admin-pro/scans.php:291 ../views-pro/scans-results.php:43
64
+ msgid "Ignore"
65
+ msgstr ""
66
+
67
+ #: ../admin-pro/scans.php:118
68
+ msgid "Confirm <b>Ignore selected results</b>"
69
+ msgstr ""
70
+
71
+ #: ../admin-pro/scans.php:129
72
+ msgid "Confirm <b>Undo Ignore this link</b>"
73
+ msgstr ""
74
+
75
+ #: ../admin-pro/scans.php:131 ../admin-pro/scans.php:142
76
+ #: ../admin-pro/scans.php:292 ../views-pro/scans-results.php:44
77
+ msgid "Undo Ignore"
78
+ msgstr ""
79
+
80
+ #: ../admin-pro/scans.php:140
81
+ msgid "Confirm <b>Undo Ignore selected results</b>"
82
+ msgstr ""
83
+
84
+ #: ../admin-pro/scans.php:151
85
+ msgid "Confirm <b>Apply Redirection</b>"
86
+ msgstr ""
87
+
88
+ #: ../admin-pro/scans.php:153
89
+ msgid "Set Redirection"
90
+ msgstr ""
91
+
92
+ #: ../admin-pro/scans.php:162
93
+ msgid "Confirm <b>Bulk apply redirections</b>"
94
+ msgstr ""
95
+
96
+ #: ../admin-pro/scans.php:164
97
+ msgid "Set Redirections"
98
+ msgstr ""
99
+
100
+ #: ../admin-pro/scans.php:173
101
+ msgid "Confirm <b>Add nofollow</b>"
102
+ msgstr ""
103
+
104
+ #: ../admin-pro/scans.php:175 ../admin-pro/scans.php:186
105
+ #: ../admin-pro/scans.php:286 ../views-pro/scans-results.php:48
106
+ msgid "Add nofollow"
107
+ msgstr ""
108
+
109
+ #: ../admin-pro/scans.php:184
110
+ msgid "Confirm <b>Bulk add nofollow</b>"
111
+ msgstr ""
112
+
113
+ #: ../admin-pro/scans.php:195
114
+ msgid "Confirm <b>Remove nofollow</b>"
115
+ msgstr ""
116
+
117
+ #: ../admin-pro/scans.php:197 ../admin-pro/scans.php:208
118
+ #: ../admin-pro/scans.php:287 ../views-pro/scans-results.php:49
119
+ msgid "Remove nofollow"
120
+ msgstr ""
121
+
122
+ #: ../admin-pro/scans.php:206
123
+ msgid "Confirm <b>Bulk remove nofollow</b>"
124
+ msgstr ""
125
+
126
+ #: ../admin-pro/scans.php:219
127
+ msgid "Update anchor text"
128
+ msgstr ""
129
+
130
+ #: ../admin-pro/scans.php:228
131
+ msgid "Confirm <b>Recheck URL status</b>"
132
+ msgstr ""
133
+
134
+ #: ../admin-pro/scans.php:230 ../admin-pro/scans.php:312
135
+ #: ../views-pro/scans-results.php:46
136
+ msgid "Recheck status"
137
+ msgstr ""
138
+
139
+ #: ../admin-pro/scans.php:237
140
+ msgid "Link headers"
141
+ msgstr ""
142
+
143
+ #: ../admin-pro/scans.php:243
144
+ msgid "Response headers"
145
+ msgstr ""
146
+
147
+ #: ../admin-pro/scans.php:245
148
+ msgid "Request headers"
149
+ msgstr ""
150
+
151
+ #: ../admin-pro/scans.php:274 ../views-pro/scans-results.php:41
152
+ msgid "Edit URL"
153
+ msgstr ""
154
+
155
+ #: ../admin-pro/scans.php:278 ../views-pro/scans-results.php:47
156
+ #: ../views/scans-results.php:910
157
+ msgid "Apply Redirection"
158
+ msgstr ""
159
+
160
+ #: ../admin-pro/scans.php:282 ../views-pro/tools-url.php:38
161
+ msgid "Remove link but leave anchor text"
162
+ msgstr ""
163
+
164
+ #: ../admin-pro/scans.php:282
165
+ msgid "Remove image from content"
166
+ msgstr ""
167
+
168
+ #: ../admin-pro/scans.php:282
169
+ msgid "Remove"
170
+ msgstr ""
171
+
172
+ #: ../admin-pro/scans.php:295 ../admin/scans.php:387
173
+ #: ../views/scans-results.php:844
174
+ msgid "Visit"
175
+ msgstr ""
176
+
177
+ #: ../admin-pro/scans.php:315
178
+ msgid "Show headers"
179
+ msgstr ""
180
+
181
+ #: ../admin-pro/scans.php:332
182
+ msgid "Edit anchor text"
183
+ msgstr ""
184
+
185
+ #: ../admin-pro/tools-url.php:23 ../admin-pro/admin.php:57
186
+ msgid "URL Tools"
187
+ msgstr ""
188
+
189
+ #: ../core/text.php:32
190
+ msgid "Scans"
191
+ msgstr ""
192
+
193
+ #: ../core/text.php:36
194
+ msgid "New scan"
195
+ msgstr ""
196
+
197
+ #: ../core/text.php:40
198
+ msgid "Add new scan"
199
+ msgstr ""
200
+
201
+ #: ../core/text.php:44 ../views/scans.php:434
202
+ msgid "Edit scan"
203
+ msgstr ""
204
+
205
+ #: ../core/text.php:48
206
+ msgid "Delete scan"
207
+ msgstr ""
208
+
209
+ #: ../core/text.php:52
210
+ msgid "Do you want to remove this scan?"
211
+ msgstr ""
212
+
213
+ #: ../core/text.php:56
214
+ msgid "Crawler action"
215
+ msgstr ""
216
+
217
+ #: ../core/text.php:60
218
+ msgid "Crawler results"
219
+ msgstr ""
220
+
221
+ #: ../core/text.php:64
222
+ msgid "Settings"
223
+ msgstr ""
224
+
225
+ #: ../core/text.php:68
226
+ #, php-format
227
+ msgid ""
228
+ "Sorry, maximum of <strong>%d running scans</strong> achieved and the scan "
229
+ "has been enqueued, it will be launched as soon as possible."
230
+ msgstr ""
231
+
232
+ #: ../core/text.php:72
233
+ #, php-format
234
+ msgid ""
235
+ "Sorry, scan not found. Please go to the <a href=\"%s\">scans list screen</a> "
236
+ "and select another scan."
237
+ msgstr ""
238
+
239
+ #: ../core/text.php:76
240
+ msgid ""
241
+ "Sorry, <strong>invalid form data</strong>. Please, <a href=\"javascript:"
242
+ "history.back();\">go back</a>, reload the last page and try again."
243
+ msgstr ""
244
+
245
+ #: ../core/text.php:80
246
+ msgid ""
247
+ "Sorry, <strong>invalid security data</strong>. Please, <a href=\"javascript:"
248
+ "history.back();\">go back</a>, reload the last page and try again."
249
+ msgstr ""
250
+
251
+ #: ../core/text.php:84
252
+ msgid "Server communication error"
253
+ msgstr ""
254
+
255
+ #: ../core/text.php:88
256
+ msgid "Unknown error"
257
+ msgstr ""
258
+
259
+ #: ../core/scans.php:236
260
+ #, php-format
261
+ msgid "%s comments"
262
+ msgstr ""
263
+
264
+ #: ../core/scans.php:238
265
+ #, php-format
266
+ msgid "%s and %s comments"
267
+ msgstr ""
268
+
269
+ #: ../core/scans.php:244 ../core/types.php:102
270
+ msgid "Blogroll"
271
+ msgstr ""
272
+
273
+ #: ../core/scans.php:362
274
+ msgid ""
275
+ "There is not any <strong>link type</strong> selected, you need to select one "
276
+ "or more."
277
+ msgstr ""
278
+
279
+ #: ../core/scans.php:371
280
+ msgid ""
281
+ "Need to select any <strong>post status</strong> value for the selected post "
282
+ "types."
283
+ msgstr ""
284
+
285
+ #: ../core/scans.php:377
286
+ msgid ""
287
+ "There is not any kind of <strong>post type</strong>, <strong>comments</"
288
+ "strong> or <strong>blogroll</strong> selected."
289
+ msgstr ""
290
+
291
+ #: ../core/scans.php:383
292
+ msgid ""
293
+ "Missing selection of any <strong>links status</strong> level or status code."
294
+ msgstr ""
295
+
296
+ #: ../core/types-curl.php:41
297
+ msgid "Ok"
298
+ msgstr ""
299
+
300
+ #: ../core/types-curl.php:41
301
+ msgid "All fine. Proceed as usual."
302
+ msgstr ""
303
+
304
+ #: ../core/types-curl.php:42
305
+ msgid "Unsupported protocol"
306
+ msgstr ""
307
+
308
+ #: ../core/types-curl.php:42
309
+ msgid ""
310
+ "The URL you passed to libcurl used a protocol that this libcurl does not "
311
+ "support. The support might be a compile-time option that you didn't use, it "
312
+ "can be a misspelled protocol string or just a protocol libcurl has no code "
313
+ "for."
314
+ msgstr ""
315
+
316
+ #: ../core/types-curl.php:43
317
+ msgid "Failed initialization"
318
+ msgstr ""
319
+
320
+ #: ../core/types-curl.php:43
321
+ msgid ""
322
+ "Very early initialization code failed. This is likely to be an internal "
323
+ "error or problem, or a resource problem where something fundamental couldn't "
324
+ "get done at init time."
325
+ msgstr ""
326
+
327
+ #: ../core/types-curl.php:44
328
+ msgid "URL malformat"
329
+ msgstr ""
330
+
331
+ #: ../core/types-curl.php:44
332
+ msgid "The URL was not properly formatted."
333
+ msgstr ""
334
+
335
+ #: ../core/types-curl.php:45
336
+ msgid "Not built-in"
337
+ msgstr ""
338
+
339
+ #: ../core/types-curl.php:45
340
+ msgid ""
341
+ "A requested feature, protocol or option was not found built-in in this "
342
+ "libcurl due to a build-time decision. This means that a feature or option "
343
+ "was not enabled or explicitly disabled when libcurl was built and in order "
344
+ "to get it to function you have to get a rebuilt libcurl."
345
+ msgstr ""
346
+
347
+ #: ../core/types-curl.php:46
348
+ msgid "Couldn't resolve proxy"
349
+ msgstr ""
350
+
351
+ #: ../core/types-curl.php:46
352
+ msgid "The given proxy host could not be resolved. "
353
+ msgstr ""
354
+
355
+ #: ../core/types-curl.php:47
356
+ msgid "Couldn't resolve host"
357
+ msgstr ""
358
+
359
+ #: ../core/types-curl.php:47
360
+ msgid "The given remote host was not resolved."
361
+ msgstr ""
362
+
363
+ #: ../core/types-curl.php:48
364
+ msgid "Couldn't connect"
365
+ msgstr ""
366
+
367
+ #: ../core/types-curl.php:48
368
+ msgid "Failed to connect() to host or proxy."
369
+ msgstr ""
370
+
371
+ #: ../core/types-curl.php:49
372
+ msgid "FTP weird server reply"
373
+ msgstr ""
374
+
375
+ #: ../core/types-curl.php:49
376
+ msgid ""
377
+ "After connecting to a FTP server, libcurl expects to get a certain reply "
378
+ "back. This error code implies that it got a strange or bad reply. The given "
379
+ "remote server is probably not an OK FTP server."
380
+ msgstr ""
381
+
382
+ #: ../core/types-curl.php:50
383
+ msgid "Remote access denied"
384
+ msgstr ""
385
+
386
+ #: ../core/types-curl.php:50
387
+ msgid ""
388
+ "We were denied access to the resource given in the URL. For FTP, this occurs "
389
+ "while trying to change to the remote directory."
390
+ msgstr ""
391
+
392
+ #: ../core/types-curl.php:51
393
+ msgid "FTP access failed"
394
+ msgstr ""
395
+
396
+ #: ../core/types-curl.php:51
397
+ msgid ""
398
+ "While waiting for the server to connect back when an active FTP session is "
399
+ "used, an error code was sent over the control connection or similar."
400
+ msgstr ""
401
+
402
+ #: ../core/types-curl.php:52
403
+ msgid "FTP weird pass reply"
404
+ msgstr ""
405
+
406
+ #: ../core/types-curl.php:52
407
+ msgid ""
408
+ "After having sent the FTP password to the server, libcurl expects a proper "
409
+ "reply. This error code indicates that an unexpected code was returned."
410
+ msgstr ""
411
+
412
+ #: ../core/types-curl.php:53
413
+ msgid "FTP accept timeout"
414
+ msgstr ""
415
+
416
+ #: ../core/types-curl.php:53
417
+ msgid ""
418
+ "During an active FTP session while waiting for the server to connect, the "
419
+ "CURLOPT_ACCEPTTIMEOUT_MS (or the internal default) timeout expired."
420
+ msgstr ""
421
+
422
+ #: ../core/types-curl.php:54
423
+ msgid "FTP weird pasv reply"
424
+ msgstr ""
425
+
426
+ #: ../core/types-curl.php:54
427
+ msgid ""
428
+ "libcurl failed to get a sensible result back from the server as a response "
429
+ "to either a PASV or a EPSV command. The server is flawed."
430
+ msgstr ""
431
+
432
+ #: ../core/types-curl.php:55
433
+ msgid "FTP weird 227 format"
434
+ msgstr ""
435
+
436
+ #: ../core/types-curl.php:55
437
+ msgid ""
438
+ "FTP servers return a 227-line as a response to a PASV command. If libcurl "
439
+ "fails to parse that line, this return code is passed back."
440
+ msgstr ""
441
+
442
+ #: ../core/types-curl.php:56
443
+ msgid "FTP can't get host"
444
+ msgstr ""
445
+
446
+ #: ../core/types-curl.php:56
447
+ msgid "An internal failure to lookup the host used for the new connection."
448
+ msgstr ""
449
+
450
+ #: ../core/types-curl.php:57
451
+ msgid "HTTP2 framing layer problem"
452
+ msgstr ""
453
+
454
+ #: ../core/types-curl.php:57
455
+ msgid ""
456
+ "A problem was detected in the HTTP2 framing layer. This is somewhat generic "
457
+ "and can be one out of several problems, see the error buffer for details."
458
+ msgstr ""
459
+
460
+ #: ../core/types-curl.php:58
461
+ msgid "FTP couldn't set type"
462
+ msgstr ""
463
+
464
+ #: ../core/types-curl.php:58
465
+ msgid ""
466
+ "Received an error when trying to set the transfer mode to binary or ASCII."
467
+ msgstr ""
468
+
469
+ #: ../core/types-curl.php:59
470
+ msgid "Partial file"
471
+ msgstr ""
472
+
473
+ #: ../core/types-curl.php:59
474
+ msgid ""
475
+ "A file transfer was shorter or larger than expected. This happens when the "
476
+ "server first reports an expected transfer size, and then delivers data that "
477
+ "doesn't match the previously given size."
478
+ msgstr ""
479
+
480
+ #: ../core/types-curl.php:60
481
+ msgid "FTP couldn't retrieve file"
482
+ msgstr ""
483
+
484
+ #: ../core/types-curl.php:60
485
+ msgid ""
486
+ "This was either a weird reply to a 'RETR' command or a zero byte transfer "
487
+ "complete."
488
+ msgstr ""
489
+
490
+ #: ../core/types-curl.php:61
491
+ msgid "Quote error"
492
+ msgstr ""
493
+
494
+ #: ../core/types-curl.php:61
495
+ msgid ""
496
+ "When sending custom 'QUOTE' commands to the remote server, one of the "
497
+ "commands returned an error code that was 400 or higher (for FTP) or "
498
+ "otherwise indicated unsuccessful completion of the command."
499
+ msgstr ""
500
+
501
+ #: ../core/types-curl.php:62
502
+ msgid "HTTP returned error"
503
+ msgstr ""
504
+
505
+ #: ../core/types-curl.php:62
506
+ msgid ""
507
+ "This is returned if CURLOPT_FAILONERROR is set TRUE and the HTTP server "
508
+ "returns an error code that is >= 400."
509
+ msgstr ""
510
+
511
+ #: ../core/types-curl.php:63
512
+ msgid "Write error"
513
+ msgstr ""
514
+
515
+ #: ../core/types-curl.php:63
516
+ msgid ""
517
+ "An error occurred when writing received data to a local file, or an error "
518
+ "was returned to libcurl from a write callback."
519
+ msgstr ""
520
+
521
+ #: ../core/types-curl.php:64
522
+ msgid "Upload failed"
523
+ msgstr ""
524
+
525
+ #: ../core/types-curl.php:64
526
+ msgid ""
527
+ "Failed starting the upload. For FTP, the server typically denied the STOR "
528
+ "command. The error buffer usually contains the server's explanation for this."
529
+ msgstr ""
530
+
531
+ #: ../core/types-curl.php:65
532
+ msgid "Read error"
533
+ msgstr ""
534
+
535
+ #: ../core/types-curl.php:65
536
+ msgid ""
537
+ "There was a problem reading a local file or an error returned by the read "
538
+ "callback."
539
+ msgstr ""
540
+
541
+ #: ../core/types-curl.php:66
542
+ msgid "Out of memory"
543
+ msgstr ""
544
+
545
+ #: ../core/types-curl.php:66
546
+ msgid ""
547
+ "A memory allocation request failed. This is serious badness and things are "
548
+ "severely screwed up if this ever occurs."
549
+ msgstr ""
550
+
551
+ #: ../core/types-curl.php:67
552
+ msgid "Operation timedout"
553
+ msgstr ""
554
+
555
+ #: ../core/types-curl.php:67
556
+ msgid "The specified time-out period was reached according to the conditions."
557
+ msgstr ""
558
+
559
+ #: ../core/types-curl.php:68
560
+ msgid "FTP port failed"
561
+ msgstr ""
562
+
563
+ #: ../core/types-curl.php:68
564
+ msgid ""
565
+ "The FTP PORT command returned error. This mostly happens when you haven't "
566
+ "specified a good enough address for libcurl to use. See CURLOPT_FTPPORT."
567
+ msgstr ""
568
+
569
+ #: ../core/types-curl.php:69
570
+ msgid "FTP couldn't use REST"
571
+ msgstr ""
572
+
573
+ #: ../core/types-curl.php:69
574
+ msgid ""
575
+ "The FTP REST command returned error. This should never happen if the server "
576
+ "is sane."
577
+ msgstr ""
578
+
579
+ #: ../core/types-curl.php:70
580
+ msgid "Range error"
581
+ msgstr ""
582
+
583
+ #: ../core/types-curl.php:70
584
+ msgid "The server does not support or accept range requests."
585
+ msgstr ""
586
+
587
+ #: ../core/types-curl.php:71
588
+ msgid "HTTP post error"
589
+ msgstr ""
590
+
591
+ #: ../core/types-curl.php:71
592
+ msgid "This is an odd error that mainly occurs due to internal confusion."
593
+ msgstr ""
594
+
595
+ #: ../core/types-curl.php:72
596
+ msgid "SSL connect error"
597
+ msgstr ""
598
+
599
+ #: ../core/types-curl.php:72
600
+ msgid ""
601
+ "A problem occurred somewhere in the SSL/TLS handshake. You really want the "
602
+ "error buffer and read the message there as it pinpoints the problem slightly "
603
+ "more. Could be certificates (file formats, paths, permissions), passwords, "
604
+ "and others."
605
+ msgstr ""
606
+
607
+ #: ../core/types-curl.php:73
608
+ msgid "Bad download resume"
609
+ msgstr ""
610
+
611
+ #: ../core/types-curl.php:73
612
+ msgid ""
613
+ "The download could not be resumed because the specified offset was out of "
614
+ "the file boundary."
615
+ msgstr ""
616
+
617
+ #: ../core/types-curl.php:74
618
+ msgid "File couldn't read file"
619
+ msgstr ""
620
+
621
+ #: ../core/types-curl.php:74
622
+ msgid ""
623
+ "A file given with FILE:// couldn't be opened. Most likely because the file "
624
+ "path doesn't identify an existing file. Did you check file permissions?"
625
+ msgstr ""
626
+
627
+ #: ../core/types-curl.php:75
628
+ msgid "LDAP cannot bind"
629
+ msgstr ""
630
+
631
+ #: ../core/types-curl.php:75
632
+ msgid "LDAP cannot bind. LDAP bind operation failed."
633
+ msgstr ""
634
+
635
+ #: ../core/types-curl.php:76
636
+ msgid "LDAP search failed"
637
+ msgstr ""
638
+
639
+ #: ../core/types-curl.php:76
640
+ msgid "LDAP search failed."
641
+ msgstr ""
642
+
643
+ #: ../core/types-curl.php:77
644
+ msgid "Function not found"
645
+ msgstr ""
646
+
647
+ #: ../core/types-curl.php:77
648
+ msgid "Function not found. A required zlib function was not found."
649
+ msgstr ""
650
+
651
+ #: ../core/types-curl.php:78
652
+ msgid "Aborted by callback"
653
+ msgstr ""
654
+
655
+ #: ../core/types-curl.php:78
656
+ msgid "Aborted by callback. A callback returned 'abort' to libcurl."
657
+ msgstr ""
658
+
659
+ #: ../core/types-curl.php:79
660
+ msgid "Bad function argument"
661
+ msgstr ""
662
+
663
+ #: ../core/types-curl.php:79
664
+ msgid "Internal error. A function was called with a bad parameter."
665
+ msgstr ""
666
+
667
+ #: ../core/types-curl.php:80
668
+ msgid "Interface failed"
669
+ msgstr ""
670
+
671
+ #: ../core/types-curl.php:80
672
+ msgid ""
673
+ "Interface error. A specified outgoing interface could not be used. Set which "
674
+ "interface to use for outgoing connections source IP address with "
675
+ "CURLOPT_INTERFACE."
676
+ msgstr ""
677
+
678
+ #: ../core/types-curl.php:81
679
+ msgid "Too many redirections"
680
+ msgstr ""
681
+
682
+ #: ../core/types-curl.php:81
683
+ msgid ""
684
+ "Too many redirects. When following redirects, libcurl hit the maximum "
685
+ "amount. Set your limit with CURLOPT_MAXREDIRS."
686
+ msgstr ""
687
+
688
+ #: ../core/types-curl.php:82
689
+ msgid "Unknown option"
690
+ msgstr ""
691
+
692
+ #: ../core/types-curl.php:82
693
+ msgid ""
694
+ "An option passed to libcurl is not recognized/known. Refer to the "
695
+ "appropriate documentation. This is most likely a problem in the program that "
696
+ "uses libcurl. The error buffer might contain more specific information about "
697
+ "which exact option it concerns."
698
+ msgstr ""
699
+
700
+ #: ../core/types-curl.php:83
701
+ msgid "Telnet option syntax"
702
+ msgstr ""
703
+
704
+ #: ../core/types-curl.php:83
705
+ msgid "A telnet option string was Illegally formatted."
706
+ msgstr ""
707
+
708
+ #: ../core/types-curl.php:84
709
+ msgid "Peer failed verification"
710
+ msgstr ""
711
+
712
+ #: ../core/types-curl.php:84
713
+ msgid ""
714
+ "The remote server's SSL certificate or SSH md5 fingerprint was deemed not OK."
715
+ msgstr ""
716
+
717
+ #: ../core/types-curl.php:85
718
+ msgid "Got nothing"
719
+ msgstr ""
720
+
721
+ #: ../core/types-curl.php:85
722
+ msgid ""
723
+ "Nothing was returned from the server, and under the circumstances, getting "
724
+ "nothing is considered an error."
725
+ msgstr ""
726
+
727
+ #: ../core/types-curl.php:86
728
+ msgid "SSL engine not found"
729
+ msgstr ""
730
+
731
+ #: ../core/types-curl.php:86
732
+ msgid "The specified crypto engine wasn't found."
733
+ msgstr ""
734
+
735
+ #: ../core/types-curl.php:87
736
+ msgid "SSL engine set failed"
737
+ msgstr ""
738
+
739
+ #: ../core/types-curl.php:87
740
+ msgid "Failed setting the selected SSL crypto engine as default!"
741
+ msgstr ""
742
+
743
+ #: ../core/types-curl.php:88
744
+ msgid "Send error"
745
+ msgstr ""
746
+
747
+ #: ../core/types-curl.php:88
748
+ msgid "Failed sending network data."
749
+ msgstr ""
750
+
751
+ #: ../core/types-curl.php:89
752
+ msgid "Receive error"
753
+ msgstr ""
754
+
755
+ #: ../core/types-curl.php:89
756
+ msgid "Failure with receiving network data."
757
+ msgstr ""
758
+
759
+ #: ../core/types-curl.php:90
760
+ msgid "Certificate problem"
761
+ msgstr ""
762
+
763
+ #: ../core/types-curl.php:90
764
+ msgid "Problem with the local client certificate. "
765
+ msgstr ""
766
+
767
+ #: ../core/types-curl.php:91
768
+ msgid "SSL cipher"
769
+ msgstr ""
770
+
771
+ #: ../core/types-curl.php:91
772
+ msgid "Couldn't use specified cipher."
773
+ msgstr ""
774
+
775
+ #: ../core/types-curl.php:92
776
+ msgid "SSL CA certificate"
777
+ msgstr ""
778
+
779
+ #: ../core/types-curl.php:92
780
+ msgid "Peer certificate cannot be authenticated with known CA certificates."
781
+ msgstr ""
782
+
783
+ #: ../core/types-curl.php:93
784
+ msgid "Bad content encoding"
785
+ msgstr ""
786
+
787
+ #: ../core/types-curl.php:93
788
+ msgid "Unrecognized transfer encoding."
789
+ msgstr ""
790
+
791
+ #: ../core/types-curl.php:94
792
+ msgid "LDAP invalid URL"
793
+ msgstr ""
794
+
795
+ #: ../core/types-curl.php:94
796
+ msgid "Invalid LDAP URL."
797
+ msgstr ""
798
+
799
+ #: ../core/types-curl.php:95
800
+ msgid "File size exceeded"
801
+ msgstr ""
802
+
803
+ #: ../core/types-curl.php:95
804
+ msgid "Maximum file size exceeded."
805
+ msgstr ""
806
+
807
+ #: ../core/types-curl.php:96
808
+ msgid "Use SSL failed"
809
+ msgstr ""
810
+
811
+ #: ../core/types-curl.php:96
812
+ msgid "Requested FTP SSL level failed. "
813
+ msgstr ""
814
+
815
+ #: ../core/types-curl.php:97
816
+ msgid "Send fail rewind"
817
+ msgstr ""
818
+
819
+ #: ../core/types-curl.php:97
820
+ msgid ""
821
+ "When doing a send operation curl had to rewind the data to retransmit, but "
822
+ "the rewinding operation failed."
823
+ msgstr ""
824
+
825
+ #: ../core/types-curl.php:98
826
+ msgid "SSL initialization failed"
827
+ msgstr ""
828
+
829
+ #: ../core/types-curl.php:98
830
+ msgid "Initiating the SSL Engine failed."
831
+ msgstr ""
832
+
833
+ #: ../core/types-curl.php:99
834
+ msgid "Login denied"
835
+ msgstr ""
836
+
837
+ #: ../core/types-curl.php:99
838
+ msgid "The remote server denied curl to login (Added in 7.13.1)"
839
+ msgstr ""
840
+
841
+ #: ../core/types-curl.php:100
842
+ msgid "TFTP file not found"
843
+ msgstr ""
844
+
845
+ #: ../core/types-curl.php:100
846
+ msgid "File not found on TFTP server."
847
+ msgstr ""
848
+
849
+ #: ../core/types-curl.php:101
850
+ msgid "TFTP permission"
851
+ msgstr ""
852
+
853
+ #: ../core/types-curl.php:101
854
+ msgid "Permission problem on TFTP server."
855
+ msgstr ""
856
+
857
+ #: ../core/types-curl.php:102
858
+ msgid "Remote disk full"
859
+ msgstr ""
860
+
861
+ #: ../core/types-curl.php:102
862
+ msgid "Out of disk space on the server."
863
+ msgstr ""
864
+
865
+ #: ../core/types-curl.php:103
866
+ msgid "TFTP illegal"
867
+ msgstr ""
868
+
869
+ #: ../core/types-curl.php:103
870
+ msgid "Illegal TFTP operation."
871
+ msgstr ""
872
+
873
+ #: ../core/types-curl.php:104
874
+ msgid "FTP unknown ID"
875
+ msgstr ""
876
+
877
+ #: ../core/types-curl.php:104
878
+ msgid "Unknown TFTP transfer ID."
879
+ msgstr ""
880
+
881
+ #: ../core/types-curl.php:105
882
+ msgid "Remote file exists"
883
+ msgstr ""
884
+
885
+ #: ../core/types-curl.php:105
886
+ msgid "File already exists and will not be overwritten."
887
+ msgstr ""
888
+
889
+ #: ../core/types-curl.php:106
890
+ msgid "TFTP no such user"
891
+ msgstr ""
892
+
893
+ #: ../core/types-curl.php:106
894
+ msgid ""
895
+ "This error should never be returned by a properly functioning TFTP server."
896
+ msgstr ""
897
+
898
+ #: ../core/types-curl.php:107
899
+ msgid "Conversion failed"
900
+ msgstr ""
901
+
902
+ #: ../core/types-curl.php:107
903
+ msgid "Character conversion failed."
904
+ msgstr ""
905
+
906
+ #: ../core/types-curl.php:108
907
+ msgid "Conversion callbacks"
908
+ msgstr ""
909
+
910
+ #: ../core/types-curl.php:108
911
+ msgid "Caller must register conversion callbacks."
912
+ msgstr ""
913
+
914
+ #: ../core/types-curl.php:109
915
+ msgid "SSL CA certificate bad file"
916
+ msgstr ""
917
+
918
+ #: ../core/types-curl.php:109
919
+ msgid "Problem with reading the SSL CA cert (path? access rights?)"
920
+ msgstr ""
921
+
922
+ #: ../core/types-curl.php:110
923
+ msgid "Remote file not found"
924
+ msgstr ""
925
+
926
+ #: ../core/types-curl.php:110
927
+ msgid "The resource referenced in the URL does not exist."
928
+ msgstr ""
929
+
930
+ #: ../core/types-curl.php:111
931
+ msgid "SSH error"
932
+ msgstr ""
933
+
934
+ #: ../core/types-curl.php:111
935
+ msgid "An unspecified error occurred during the SSH session."
936
+ msgstr ""
937
+
938
+ #: ../core/types-curl.php:112
939
+ msgid "SSL shutdown failed"
940
+ msgstr ""
941
+
942
+ #: ../core/types-curl.php:112
943
+ msgid "Failed to shut down the SSL connection."
944
+ msgstr ""
945
+
946
+ #: ../core/types-curl.php:113
947
+ msgid "Socket not ready"
948
+ msgstr ""
949
+
950
+ #: ../core/types-curl.php:113
951
+ msgid ""
952
+ "Socket is not ready for send/recv wait till it's ready and try again. This "
953
+ "return code is only returned from curl_easy_recv and curl_easy_send (Added "
954
+ "in 7.18.2)"
955
+ msgstr ""
956
+
957
+ #: ../core/types-curl.php:114
958
+ msgid "SSL CRL bad file"
959
+ msgstr ""
960
+
961
+ #: ../core/types-curl.php:114
962
+ msgid "Failed to load CRL file (Added in 7.19.0)"
963
+ msgstr ""
964
+
965
+ #: ../core/types-curl.php:115
966
+ msgid "SSL issue error"
967
+ msgstr ""
968
+
969
+ #: ../core/types-curl.php:115
970
+ msgid "Issuer check failed (Added in 7.19.0)"
971
+ msgstr ""
972
+
973
+ #: ../core/types-curl.php:116
974
+ msgid "FTP PRET failed"
975
+ msgstr ""
976
+
977
+ #: ../core/types-curl.php:116
978
+ msgid ""
979
+ "The FTP server does not understand the PRET command at all or does not "
980
+ "support the given argument. Be careful when using CURLOPT_CUSTOMREQUEST, a "
981
+ "custom LIST command will be sent with PRET CMD before PASV as well. (Added "
982
+ "in 7.20.0)"
983
+ msgstr ""
984
+
985
+ #: ../core/types-curl.php:117
986
+ msgid "RTSP CSEQ error"
987
+ msgstr ""
988
+
989
+ #: ../core/types-curl.php:117
990
+ msgid "Mismatch of RTSP CSeq numbers."
991
+ msgstr ""
992
+
993
+ #: ../core/types-curl.php:118
994
+ msgid "RTSP session error"
995
+ msgstr ""
996
+
997
+ #: ../core/types-curl.php:118
998
+ msgid "Mismatch of RTSP Session Identifiers."
999
+ msgstr ""
1000
+
1001
+ #: ../core/types-curl.php:119
1002
+ msgid "FTP bad file list"
1003
+ msgstr ""
1004
+
1005
+ #: ../core/types-curl.php:119
1006
+ msgid "Unable to parse FTP file list (during FTP wildcard downloading)."
1007
+ msgstr ""
1008
+
1009
+ #: ../core/types-curl.php:120
1010
+ msgid "Chunk failed"
1011
+ msgstr ""
1012
+
1013
+ #: ../core/types-curl.php:120
1014
+ msgid "Chunk callback reported error."
1015
+ msgstr ""
1016
+
1017
+ #: ../core/types-curl.php:121
1018
+ msgid "No connection available"
1019
+ msgstr ""
1020
+
1021
+ #: ../core/types-curl.php:121
1022
+ msgid ""
1023
+ "(For internal use only, will never be returned by libcurl) No connection "
1024
+ "available, the session will be queued. (added in 7.30.0)"
1025
+ msgstr ""
1026
+
1027
+ #: ../core/boot.php:14
1028
+ msgid ""
1029
+ "Detected another version of WP Link Status already active. Please deactivate "
1030
+ "it before and try again to activate this plugin."
1031
+ msgstr ""
1032
+
1033
+ #: ../core/boot.php:20
1034
+ msgid "Sorry, this version of WP Link Status requires WordPress 3.3 or later."
1035
+ msgstr ""
1036
+
1037
+ #: ../core/notify.php:91
1038
+ msgid "WP Link Status scan completed"
1039
+ msgstr ""
1040
+
1041
+ #: ../core/notify.php:92
1042
+ #, php-format
1043
+ msgid ""
1044
+ "\n"
1045
+ "\n"
1046
+ "Your scan is completed, you can see the results here:\n"
1047
+ "\n"
1048
+ "%s\n"
1049
+ "%s\n"
1050
+ "\n"
1051
+ msgstr ""
1052
+
1053
+ #: ../core/notify.php:99 ../views/scans.php:375
1054
+ msgid "(no name)"
1055
+ msgstr ""
1056
+
1057
+ #: ../core/types.php:100
1058
+ msgid "Entries"
1059
+ msgstr ""
1060
+
1061
+ #: ../core/types.php:101
1062
+ msgid "Comments"
1063
+ msgstr ""
1064
+
1065
+ #: ../core/types.php:113
1066
+ msgid "Waiting"
1067
+ msgstr ""
1068
+
1069
+ #: ../core/types.php:114
1070
+ msgid "Queued"
1071
+ msgstr ""
1072
+
1073
+ #: ../core/types.php:115
1074
+ msgid "Running"
1075
+ msgstr ""
1076
+
1077
+ #: ../core/types.php:116
1078
+ msgid "Stopped"
1079
+ msgstr ""
1080
+
1081
+ #: ../core/types.php:117
1082
+ msgid "Completed"
1083
+ msgstr ""
1084
+
1085
+ #: ../core/types.php:128
1086
+ msgid "All URLs"
1087
+ msgstr ""
1088
+
1089
+ #: ../core/types.php:129
1090
+ msgid "Internal URLs"
1091
+ msgstr ""
1092
+
1093
+ #: ../core/types.php:130
1094
+ msgid "External URLs"
1095
+ msgstr ""
1096
+
1097
+ #: ../core/types.php:141
1098
+ msgid "Anytime content"
1099
+ msgstr ""
1100
+
1101
+ #: ../core/types.php:142
1102
+ msgid "From yesterday"
1103
+ msgstr ""
1104
+
1105
+ #: ../core/types.php:143
1106
+ msgid "Last 7 days"
1107
+ msgstr ""
1108
+
1109
+ #: ../core/types.php:144
1110
+ msgid "Last 15 days"
1111
+ msgstr ""
1112
+
1113
+ #: ../core/types.php:145
1114
+ msgid "One month"
1115
+ msgstr ""
1116
+
1117
+ #: ../core/types.php:146
1118
+ msgid "Last 3 months"
1119
+ msgstr ""
1120
+
1121
+ #: ../core/types.php:147
1122
+ msgid "Last 6 months"
1123
+ msgstr ""
1124
+
1125
+ #: ../core/types.php:148
1126
+ msgid "One year"
1127
+ msgstr ""
1128
+
1129
+ #: ../core/types.php:160
1130
+ msgid "Links"
1131
+ msgstr ""
1132
+
1133
+ #: ../core/types.php:161
1134
+ msgid "Images"
1135
+ msgstr ""
1136
+
1137
+ #: ../core/types.php:172
1138
+ msgid "Most recent content"
1139
+ msgstr ""
1140
+
1141
+ #: ../core/types.php:173
1142
+ msgid "Oldest content first"
1143
+ msgstr ""
1144
+
1145
+ #: ../core/types.php:196
1146
+ msgid "Approved"
1147
+ msgstr ""
1148
+
1149
+ #: ../core/types.php:197
1150
+ msgid "Pending"
1151
+ msgstr ""
1152
+
1153
+ #: ../core/types.php:223 ../core/types.php:295
1154
+ msgid "Contains"
1155
+ msgstr ""
1156
+
1157
+ #: ../core/types.php:224 ../core/types.php:296
1158
+ msgid "Not contains"
1159
+ msgstr ""
1160
+
1161
+ #: ../core/types.php:225
1162
+ msgid "Is equal to"
1163
+ msgstr ""
1164
+
1165
+ #: ../core/types.php:226 ../core/types.php:298
1166
+ msgid "Not equal to"
1167
+ msgstr ""
1168
+
1169
+ #: ../core/types.php:227 ../core/types.php:241
1170
+ msgid "Starts with"
1171
+ msgstr ""
1172
+
1173
+ #: ../core/types.php:228 ../core/types.php:242
1174
+ msgid "Ends by"
1175
+ msgstr ""
1176
+
1177
+ #: ../core/types.php:229 ../core/types.php:300
1178
+ msgid "Empty"
1179
+ msgstr ""
1180
+
1181
+ #: ../core/types.php:240 ../core/types.php:254 ../core/types.php:268
1182
+ msgid "Matched string"
1183
+ msgstr ""
1184
+
1185
+ #: ../core/types.php:243
1186
+ msgid "Full anchor"
1187
+ msgstr ""
1188
+
1189
+ #: ../core/types.php:255 ../core/types.php:269
1190
+ msgid "URL prefix"
1191
+ msgstr ""
1192
+
1193
+ #: ../core/types.php:256 ../core/types.php:270
1194
+ msgid "URL suffix"
1195
+ msgstr ""
1196
+
1197
+ #: ../core/types.php:257 ../core/types.php:272
1198
+ msgid "Full URL"
1199
+ msgstr ""
1200
+
1201
+ #: ../core/types.php:271
1202
+ msgid "URL fragment #"
1203
+ msgstr ""
1204
+
1205
+ #: ../core/types.php:283
1206
+ msgid "Has"
1207
+ msgstr ""
1208
+
1209
+ #: ../core/types.php:284
1210
+ msgid "Not have"
1211
+ msgstr ""
1212
+
1213
+ #: ../core/types.php:297
1214
+ msgid "Equals"
1215
+ msgstr ""
1216
+
1217
+ #: ../core/types.php:299
1218
+ msgid "Not empty"
1219
+ msgstr ""
1220
+
1221
+ #: ../core/types.php:311
1222
+ msgid "Success"
1223
+ msgstr ""
1224
+
1225
+ #: ../core/types.php:312
1226
+ msgid "Redirections"
1227
+ msgstr ""
1228
+
1229
+ #: ../core/types.php:313
1230
+ msgid "Errors"
1231
+ msgstr ""
1232
+
1233
+ #: ../core/types.php:314
1234
+ msgid "Server Error"
1235
+ msgstr ""
1236
+
1237
+ #: ../core/types.php:326
1238
+ msgid "OK"
1239
+ msgstr ""
1240
+
1241
+ #: ../core/types.php:327
1242
+ msgid "Created"
1243
+ msgstr ""
1244
+
1245
+ #: ../core/types.php:328
1246
+ msgid "Accepted"
1247
+ msgstr ""
1248
+
1249
+ #: ../core/types.php:329
1250
+ msgid "Non-Authoritative Information"
1251
+ msgstr ""
1252
+
1253
+ #: ../core/types.php:330
1254
+ msgid "No Content"
1255
+ msgstr ""
1256
+
1257
+ #: ../core/types.php:331
1258
+ msgid "Reset Content"
1259
+ msgstr ""
1260
+
1261
+ #: ../core/types.php:332
1262
+ msgid "Partial Content"
1263
+ msgstr ""
1264
+
1265
+ #: ../core/types.php:335
1266
+ msgid "Multiple Choices"
1267
+ msgstr ""
1268
+
1269
+ #: ../core/types.php:336
1270
+ msgid "Moved Permanently"
1271
+ msgstr ""
1272
+
1273
+ #: ../core/types.php:337
1274
+ msgid "Found"
1275
+ msgstr ""
1276
+
1277
+ #: ../core/types.php:338
1278
+ msgid "See Other"
1279
+ msgstr ""
1280
+
1281
+ #: ../core/types.php:339
1282
+ msgid "Not Modified"
1283
+ msgstr ""
1284
+
1285
+ #: ../core/types.php:340
1286
+ msgid "Use Proxy"
1287
+ msgstr ""
1288
+
1289
+ #: ../core/types.php:341
1290
+ msgid "Temporary Redirect"
1291
+ msgstr ""
1292
+
1293
+ #: ../core/types.php:344
1294
+ msgid "Bad Request"
1295
+ msgstr ""
1296
+
1297
+ #: ../core/types.php:345
1298
+ msgid "Unauthorized"
1299
+ msgstr ""
1300
+
1301
+ #: ../core/types.php:346
1302
+ msgid "Payment Required"
1303
+ msgstr ""
1304
+
1305
+ #: ../core/types.php:347
1306
+ msgid "Forbidden"
1307
+ msgstr ""
1308
+
1309
+ #: ../core/types.php:348
1310
+ msgid "Not Found"
1311
+ msgstr ""
1312
+
1313
+ #: ../core/types.php:349
1314
+ msgid "Method Not Allowed"
1315
+ msgstr ""
1316
+
1317
+ #: ../core/types.php:350
1318
+ msgid "Not Acceptable"
1319
+ msgstr ""
1320
+
1321
+ #: ../core/types.php:351
1322
+ msgid "Proxy Authentication Required"
1323
+ msgstr ""
1324
+
1325
+ #: ../core/types.php:352
1326
+ msgid "Request Timeout"
1327
+ msgstr ""
1328
+
1329
+ #: ../core/types.php:353
1330
+ msgid "Conflict"
1331
+ msgstr ""
1332
+
1333
+ #: ../core/types.php:354
1334
+ msgid "Gone"
1335
+ msgstr ""
1336
+
1337
+ #: ../core/types.php:355
1338
+ msgid "Length Required"
1339
+ msgstr ""
1340
+
1341
+ #: ../core/types.php:356
1342
+ msgid "Precondition Failed"
1343
+ msgstr ""
1344
+
1345
+ #: ../core/types.php:357
1346
+ msgid "Request Entity Too Large"
1347
+ msgstr ""
1348
+
1349
+ #: ../core/types.php:358
1350
+ msgid "Request-URI Too Long"
1351
+ msgstr ""
1352
+
1353
+ #: ../core/types.php:359
1354
+ msgid "Unsupported Media Type"
1355
+ msgstr ""
1356
+
1357
+ #: ../core/types.php:360
1358
+ msgid "Requested Range Not Satisfiable"
1359
+ msgstr ""
1360
+
1361
+ #: ../core/types.php:361
1362
+ msgid "Expectation Failed"
1363
+ msgstr ""
1364
+
1365
+ #: ../core/types.php:364
1366
+ msgid "Internal Server Error"
1367
+ msgstr ""
1368
+
1369
+ #: ../core/types.php:365
1370
+ msgid "Not Implemented"
1371
+ msgstr ""
1372
+
1373
+ #: ../core/types.php:366
1374
+ msgid "Bad Gateway"
1375
+ msgstr ""
1376
+
1377
+ #: ../core/types.php:367
1378
+ msgid "Service Unavailable"
1379
+ msgstr ""
1380
+
1381
+ #: ../core/types.php:368
1382
+ msgid "Gateway Timeout"
1383
+ msgstr ""
1384
+
1385
+ #: ../core/types.php:369
1386
+ msgid "HTTP Version Not Supported"
1387
+ msgstr ""
1388
+
1389
+ #: ../core/types.php:396
1390
+ msgid "NoFollow"
1391
+ msgstr ""
1392
+
1393
+ #: ../core/types.php:397
1394
+ msgid "DoFollow"
1395
+ msgstr ""
1396
+
1397
+ #: ../core/types.php:421 ../views/scans-results.php:265
1398
+ msgid "Relative"
1399
+ msgstr ""
1400
+
1401
+ #: ../core/types.php:422 ../views/scans-results.php:266
1402
+ msgid "Absolute"
1403
+ msgstr ""
1404
+
1405
+ #: ../core/types.php:423 ../views/scans-results.php:267
1406
+ msgid "Spaced"
1407
+ msgstr ""
1408
+
1409
+ #: ../core/types.php:424 ../views/scans.php:396 ../views/scans-results.php:268
1410
+ msgid "Malformed"
1411
+ msgstr ""
1412
+
1413
+ #: ../core/types.php:435 ../views/scans-results.php:182
1414
+ msgid "Unlinked"
1415
+ msgstr ""
1416
+
1417
+ #: ../core/types.php:436 ../views/scans-results.php:263
1418
+ #: ../views/scans-results.php:332
1419
+ msgid "Modified"
1420
+ msgstr ""
1421
+
1422
+ #: ../core/types.php:437
1423
+ msgid "Unmodified"
1424
+ msgstr ""
1425
+
1426
+ #: ../core/types.php:438
1427
+ msgid "Rechecked"
1428
+ msgstr ""
1429
+
1430
+ #: ../core/types.php:449
1431
+ msgid "Only ignored results"
1432
+ msgstr ""
1433
+
1434
+ #: ../core/types.php:450
1435
+ msgid "Ignored and not ignored"
1436
+ msgstr ""
1437
+
1438
+ #: ../core/types.php:461
1439
+ msgid "Domain name ASC"
1440
+ msgstr ""
1441
+
1442
+ #: ../core/types.php:462
1443
+ msgid "Domain name DESC"
1444
+ msgstr ""
1445
+
1446
+ #: ../core/types.php:463
1447
+ msgid "Download time ASC"
1448
+ msgstr ""
1449
+
1450
+ #: ../core/types.php:464
1451
+ msgid "Download time DESC"
1452
+ msgstr ""
1453
+
1454
+ #: ../core/types.php:465
1455
+ msgid "Download size ASC"
1456
+ msgstr ""
1457
+
1458
+ #: ../core/types.php:466
1459
+ msgid "Download size DESC"
1460
+ msgstr ""
1461
+
1462
+ #: ../core/module.php:143
1463
+ msgid "Sorry, current user can`t perform this action"
1464
+ msgstr ""
1465
+
1466
+ #: ../core/module.php:149
1467
+ msgid ""
1468
+ "Sorry, security verification error. Please reload this page and try again."
1469
+ msgstr ""
1470
+
1471
+ #: ../views-pro/tools-url.php:21
1472
+ msgid "WordPress database will be updated, press Ok to continue"
1473
+ msgstr ""
1474
+
1475
+ #: ../views-pro/tools-url.php:21
1476
+ msgid "Sorry, no links detected. Please enter some URLs."
1477
+ msgstr ""
1478
+
1479
+ #: ../views-pro/tools-url.php:21
1480
+ msgid "Processing"
1481
+ msgstr ""
1482
+
1483
+ #: ../views-pro/tools-url.php:21
1484
+ msgid "Updating database"
1485
+ msgstr ""
1486
+
1487
+ #: ../views-pro/tools-url.php:21
1488
+ msgid "Test mode, no database changes"
1489
+ msgstr ""
1490
+
1491
+ #: ../views-pro/tools-url.php:21
1492
+ msgid "No found entries"
1493
+ msgstr ""
1494
+
1495
+ #: ../views-pro/tools-url.php:21
1496
+ msgid "Cancelling"
1497
+ msgstr ""
1498
+
1499
+ #: ../views-pro/tools-url.php:21
1500
+ msgid "Cancelled"
1501
+ msgstr ""
1502
+
1503
+ #: ../views-pro/tools-url.php:21 ../views-pro/tools-url.php:59
1504
+ msgid "Finished"
1505
+ msgstr ""
1506
+
1507
+ #: ../views-pro/tools-url.php:21
1508
+ msgid "Processed"
1509
+ msgstr ""
1510
+
1511
+ #: ../views-pro/tools-url.php:21
1512
+ msgid "of"
1513
+ msgstr ""
1514
+
1515
+ #: ../views-pro/tools-url.php:21
1516
+ msgid "links"
1517
+ msgstr ""
1518
+
1519
+ #: ../views-pro/tools-url.php:21
1520
+ msgid "Back to the form"
1521
+ msgstr ""
1522
+
1523
+ #: ../views-pro/tools-url.php:27
1524
+ msgid "Enter here the URLs, one per line:"
1525
+ msgstr ""
1526
+
1527
+ #: ../views-pro/tools-url.php:34
1528
+ msgid "Select operation"
1529
+ msgstr ""
1530
+
1531
+ #: ../views-pro/tools-url.php:36
1532
+ msgid "Add link rel=&quot;nofollow&quot;"
1533
+ msgstr ""
1534
+
1535
+ #: ../views-pro/tools-url.php:37
1536
+ msgid "Remove &quot;nofollow&quot; from rel property"
1537
+ msgstr ""
1538
+
1539
+ #: ../views-pro/tools-url.php:39
1540
+ msgid "Replace URL by its 301 redirection"
1541
+ msgstr ""
1542
+
1543
+ #: ../views-pro/tools-url.php:40
1544
+ msgid "Remove &lt;object&gt; containing URL"
1545
+ msgstr ""
1546
+
1547
+ #: ../views-pro/tools-url.php:43
1548
+ msgid "Test mode, no database updates"
1549
+ msgstr ""
1550
+
1551
+ #: ../views-pro/tools-url.php:44
1552
+ msgid "Update changes in database"
1553
+ msgstr ""
1554
+
1555
+ #: ../views-pro/tools-url.php:49
1556
+ msgid "Execute Test Process"
1557
+ msgstr ""
1558
+
1559
+ #: ../views-pro/tools-url.php:51
1560
+ msgid "Execute Database Update"
1561
+ msgstr ""
1562
+
1563
+ #: ../views-pro/tools-url.php:64 ../views-pro/tools-url.php:71
1564
+ msgid "Link"
1565
+ msgstr ""
1566
+
1567
+ #: ../views-pro/tools-url.php:65 ../views-pro/tools-url.php:72
1568
+ #: ../views/scans-results.php:113 ../views/scans-edit.php:342
1569
+ #: ../views/scans-edit.php:343 ../views/scans-edit.php:352
1570
+ msgid "Anchor text"
1571
+ msgstr ""
1572
+
1573
+ #: ../views-pro/tools-url.php:66 ../views-pro/tools-url.php:73
1574
+ msgid "Result"
1575
+ msgstr ""
1576
+
1577
+ #: ../views-pro/scans-results.php:45
1578
+ msgid "Edit anchor"
1579
+ msgstr ""
1580
+
1581
+ #: ../views-pro/scans-results.php:88
1582
+ msgid "Toggle to the advanced search panel"
1583
+ msgstr ""
1584
+
1585
+ #: ../views-pro/scans-results.php:88
1586
+ msgid "Advanced Search"
1587
+ msgstr ""
1588
+
1589
+ #: ../views-pro/scans-results.php:91
1590
+ msgid "Bulk unlink URLs"
1591
+ msgstr ""
1592
+
1593
+ #: ../views-pro/scans-results.php:91
1594
+ msgid "Bulk ignore results"
1595
+ msgstr ""
1596
+
1597
+ #: ../views-pro/scans-results.php:91
1598
+ msgid "Bulk undo ignore results"
1599
+ msgstr ""
1600
+
1601
+ #: ../views-pro/scans-results.php:91
1602
+ msgid "Bulk anchor text edition"
1603
+ msgstr ""
1604
+
1605
+ #: ../views-pro/scans-results.php:91
1606
+ msgid "Bulk URL edition"
1607
+ msgstr ""
1608
+
1609
+ #: ../views-pro/scans-results.php:91
1610
+ msgid "Bulk URL recheck status"
1611
+ msgstr ""
1612
+
1613
+ #: ../views-pro/scans-results.php:91
1614
+ msgid "Bulk apply redirections"
1615
+ msgstr ""
1616
+
1617
+ #: ../views-pro/scans-results.php:91
1618
+ msgid "Bulk add nofollow"
1619
+ msgstr ""
1620
+
1621
+ #: ../views-pro/scans-results.php:91
1622
+ msgid "Bulk remove nofollow"
1623
+ msgstr ""
1624
+
1625
+ #: ../views-pro/scans-results.php:120
1626
+ msgid "Common filters"
1627
+ msgstr ""
1628
+
1629
+ #: ../views-pro/scans-results.php:130
1630
+ msgid "Extended"
1631
+ msgstr ""
1632
+
1633
+ #: ../views-pro/scans-results.php:158
1634
+ msgid "Filter Results"
1635
+ msgstr ""
1636
+
1637
+ #: ../views-pro/scans-results.php:170
1638
+ msgid "Close advanced search panel"
1639
+ msgstr ""
1640
+
1641
+ #: ../views-pro/scans-results.php:171
1642
+ msgid "Reset advanced search fields"
1643
+ msgstr ""
1644
+
1645
+ #: ../views-pro/scans-results.php:171
1646
+ msgid "Reset advanced search fields?"
1647
+ msgstr ""
1648
+
1649
+ #: ../views-pro/scans-results.php:199
1650
+ msgid "Not ignored results"
1651
+ msgstr ""
1652
+
1653
+ #: ../views-pro/scans-results.php:229
1654
+ msgid "SEO links"
1655
+ msgstr ""
1656
+
1657
+ #: ../views-pro/scans-results.php:244
1658
+ msgid "Protocol"
1659
+ msgstr ""
1660
+
1661
+ #: ../views-pro/scans-results.php:259
1662
+ msgid "Special"
1663
+ msgstr ""
1664
+
1665
+ #: ../views-pro/scans-results.php:274
1666
+ msgid "Action"
1667
+ msgstr ""
1668
+
1669
+ #: ../views-pro/scans-results.php:291
1670
+ msgid "External and internal URLs"
1671
+ msgstr ""
1672
+
1673
+ #: ../admin/scans.php:473
1674
+ msgid "The crawling process for this scan is already started."
1675
+ msgstr ""
1676
+
1677
+ #: ../admin/scans.php:479
1678
+ msgid "The crawling process for this scan is already stopped."
1679
+ msgstr ""
1680
+
1681
+ #: ../admin/scans.php:485
1682
+ msgid "This scan was ended and is not possible to start again."
1683
+ msgstr ""
1684
+
1685
+ #: ../admin/scans.php:500
1686
+ msgid "Something went wrong and the unqueue process was failed."
1687
+ msgstr ""
1688
+
1689
+ #: ../admin/scans.php:506
1690
+ msgid "The crawler for this scan is back to the wait mode."
1691
+ msgstr ""
1692
+
1693
+ #: ../admin/scans.php:516
1694
+ msgid "Something went wront and the crawler stop was failed."
1695
+ msgstr ""
1696
+
1697
+ #: ../admin/scans.php:522
1698
+ #, php-format
1699
+ msgid ""
1700
+ "The crawler for this scan is stopped. You can see its collected data in the "
1701
+ "<a href=\"%s\">crawler results page</a>."
1702
+ msgstr ""
1703
+
1704
+ #: ../admin/scans.php:542
1705
+ #, php-format
1706
+ msgid ""
1707
+ "You need to complete some critical values before start the crawler, please "
1708
+ "<a href=\"%s\">edit this scan</a>."
1709
+ msgstr ""
1710
+
1711
+ #: ../admin/scans.php:577
1712
+ msgid "Something went wront and the crawler start was failed."
1713
+ msgstr ""
1714
+
1715
+ #: ../admin/scans.php:597
1716
+ #, php-format
1717
+ msgid ""
1718
+ "The crawler is running, you can see its data in the <a href=\"%s\">crawler "
1719
+ "results page</a>."
1720
+ msgstr ""
1721
+
1722
+ #: ../admin/scans.php:668
1723
+ #, php-format
1724
+ msgid ""
1725
+ "Sorry, we need a confirmation action. Please click here to <a href=\"%s\" "
1726
+ "class=\"wplnst-scan-delete\" data-confirm=\"%s\">delete scan</a>"
1727
+ msgstr ""
1728
+
1729
+ #: ../admin/scans.php:695
1730
+ msgid "The scans have been removed."
1731
+ msgstr ""
1732
+
1733
+ #: ../admin/scans.php:713
1734
+ #, php-format
1735
+ msgid ""
1736
+ "Sorry, we need a confirmation action. Please click here to <a href=\"%s\" "
1737
+ "class=\"wplnst-scan-delete-isolated\" data-confirm-delete=\"%s\">delete "
1738
+ "scan</a>"
1739
+ msgstr ""
1740
+
1741
+ #: ../admin/scans.php:722
1742
+ msgid "The scan has been removed."
1743
+ msgstr ""
1744
+
1745
+ #: ../admin/scans.php:751
1746
+ msgid "Back to the scans list"
1747
+ msgstr ""
1748
+
1749
+ #: ../admin/scans.php:794
1750
+ msgid "New scan added successfully."
1751
+ msgstr ""
1752
+
1753
+ #: ../admin/scans.php:809
1754
+ msgid "Something went wrong trying to start the crawler for this new scan."
1755
+ msgstr ""
1756
+
1757
+ #: ../admin/scans.php:815
1758
+ #, php-format
1759
+ msgid ""
1760
+ "The crawler for this new scan is running. You can see its data in the <a "
1761
+ "href=\"%s\">crawler results page</a>."
1762
+ msgstr ""
1763
+
1764
+ #: ../admin/scans.php:823
1765
+ #, php-format
1766
+ msgid "From now on you can <a href=\"%s\">start the crawler</a>."
1767
+ msgstr ""
1768
+
1769
+ #: ../admin/scans-submit.php:143
1770
+ msgid ""
1771
+ "Something went wrong adding the new scan. Please <a href=\"javascript:"
1772
+ "history.back();\">go back</a> and attempt again to submit form."
1773
+ msgstr ""
1774
+
1775
+ #: ../admin/scans-submit.php:219
1776
+ msgid ""
1777
+ "Something went wrong updating the scan data. Please <a href=\"javascript:"
1778
+ "history.back();\">go back</a> and attempt again to save data."
1779
+ msgstr ""
1780
+
1781
+ #: ../admin/scans-submit.php:225
1782
+ msgid "Scan updated successfully."
1783
+ msgstr ""
1784
+
1785
+ #: ../admin/scans-submit.php:243
1786
+ msgid "Something went wrong trying to start the crawler."
1787
+ msgstr ""
1788
+
1789
+ #: ../admin/scans-submit.php:259
1790
+ #, php-format
1791
+ msgid ""
1792
+ "The crawler fot this scan is running. You can see its data in the <a href="
1793
+ "\"%s\">crawler results page</a>."
1794
+ msgstr ""
1795
+
1796
+ #: ../admin/scans-submit.php:267
1797
+ #, php-format
1798
+ msgid ""
1799
+ "The crawler for this scan is <strong>not started</strong>, you can <a href="
1800
+ "\"%s\">start the crawler</a> now."
1801
+ msgstr ""
1802
+
1803
+ #: ../admin/settings.php:52
1804
+ msgid "Settings updated"
1805
+ msgstr ""
1806
+
1807
+ #: ../admin/admin.php:256
1808
+ msgid "Scans per page"
1809
+ msgstr ""
1810
+
1811
+ #: ../admin/admin.php:262
1812
+ msgid "Crawler results per page"
1813
+ msgstr ""
1814
+
1815
+ #: ../admin/admin.php:341
1816
+ msgid ""
1817
+ "Not detected the required cURL module enabled. Please contact with your "
1818
+ "hosting provider in order to install cURL for PHP in this server."
1819
+ msgstr ""
1820
+
1821
+ #: ../views/scans.php:105
1822
+ msgid "Scan info"
1823
+ msgstr ""
1824
+
1825
+ #: ../views/scans.php:106
1826
+ msgid "Configuration"
1827
+ msgstr ""
1828
+
1829
+ #: ../views/scans.php:121
1830
+ msgid "Some critical values of this scan are not completed"
1831
+ msgstr ""
1832
+
1833
+ #: ../views/scans.php:134
1834
+ msgid "Waiting..."
1835
+ msgstr ""
1836
+
1837
+ #: ../views/scans.php:144
1838
+ msgid "entries"
1839
+ msgstr ""
1840
+
1841
+ #: ../views/scans.php:152
1842
+ msgid "comments"
1843
+ msgstr ""
1844
+
1845
+ #: ../views/scans.php:159
1846
+ msgid "blogroll"
1847
+ msgstr ""
1848
+
1849
+ #: ../views/scans.php:190
1850
+ #, php-format
1851
+ msgid "Today from %s"
1852
+ msgstr ""
1853
+
1854
+ #: ../views/scans.php:196
1855
+ #, php-format
1856
+ msgid "Yesterday at %s"
1857
+ msgstr ""
1858
+
1859
+ #: ../views/scans.php:202
1860
+ #, php-format
1861
+ msgid "%s at %s"
1862
+ msgstr ""
1863
+
1864
+ #: ../views/scans.php:228 ../views/scans.php:244
1865
+ #, php-format
1866
+ msgid "to %s"
1867
+ msgstr ""
1868
+
1869
+ #: ../views/scans.php:234
1870
+ #, php-format
1871
+ msgid "to today at %s"
1872
+ msgstr ""
1873
+
1874
+ #: ../views/scans.php:250
1875
+ #, php-format
1876
+ msgid "to yesterday at %s"
1877
+ msgstr ""
1878
+
1879
+ #: ../views/scans.php:257
1880
+ #, php-format
1881
+ msgid "until %s"
1882
+ msgstr ""
1883
+
1884
+ #: ../views/scans.php:263
1885
+ #, php-format
1886
+ msgid "until %s at %s"
1887
+ msgstr ""
1888
+
1889
+ #: ../views/scans.php:284
1890
+ #, php-format
1891
+ msgid "Running time %s"
1892
+ msgstr ""
1893
+
1894
+ #: ../views/scans.php:306
1895
+ #, php-format
1896
+ msgid "<strong>%s</strong> results"
1897
+ msgstr ""
1898
+
1899
+ #: ../views/scans.php:306
1900
+ msgid "No results"
1901
+ msgstr ""
1902
+
1903
+ #: ../views/scans.php:312
1904
+ #, php-format
1905
+ msgid "<strong>%s</strong> unique URLs"
1906
+ msgstr ""
1907
+
1908
+ #: ../views/scans.php:317
1909
+ #, php-format
1910
+ msgid "%s enqueued"
1911
+ msgstr ""
1912
+
1913
+ #: ../views/scans.php:321
1914
+ #, php-format
1915
+ msgid "%s processing"
1916
+ msgstr ""
1917
+
1918
+ #: ../views/scans.php:325
1919
+ #, php-format
1920
+ msgid "<strong>%s</strong> Request error"
1921
+ msgstr ""
1922
+
1923
+ #: ../views/scans.php:352
1924
+ #, php-format
1925
+ msgid "Created today at %s"
1926
+ msgstr ""
1927
+
1928
+ #: ../views/scans.php:358
1929
+ #, php-format
1930
+ msgid "Created yesterday at %s"
1931
+ msgstr ""
1932
+
1933
+ #: ../views/scans.php:364
1934
+ #, php-format
1935
+ msgid "Created %s at %s"
1936
+ msgstr ""
1937
+
1938
+ #: ../views/scans.php:380
1939
+ #, php-format
1940
+ msgid "Ready to <a href=\"%s\">start the crawler</a>"
1941
+ msgstr ""
1942
+
1943
+ #: ../views/scans.php:396
1944
+ msgid "Scope"
1945
+ msgstr ""
1946
+
1947
+ #: ../views/scans.php:396
1948
+ msgid "Order by"
1949
+ msgstr ""
1950
+
1951
+ #: ../views/scans.php:396
1952
+ msgid "Check redirection status"
1953
+ msgstr ""
1954
+
1955
+ #: ../views/scans.php:396
1956
+ msgid "Redirections not checked"
1957
+ msgstr ""
1958
+
1959
+ #: ../views/scans.php:399 ../views/scans-edit.php:245
1960
+ #: ../views/scans-edit.php:252
1961
+ msgid "Post types"
1962
+ msgstr ""
1963
+
1964
+ #: ../views/scans.php:399 ../views/scans-edit.php:266
1965
+ #: ../views/scans-edit.php:273
1966
+ msgid "Post status"
1967
+ msgstr ""
1968
+
1969
+ #: ../views/scans.php:402
1970
+ msgid "Link status"
1971
+ msgstr ""
1972
+
1973
+ #: ../views/scans.php:431
1974
+ msgid "Show results"
1975
+ msgstr ""
1976
+
1977
+ #: ../views/scans.php:438
1978
+ msgid "Start crawler"
1979
+ msgstr ""
1980
+
1981
+ #: ../views/scans.php:438
1982
+ msgid "Unqueue crawling"
1983
+ msgstr ""
1984
+
1985
+ #: ../views/scans.php:438
1986
+ msgid "Stop crawler"
1987
+ msgstr ""
1988
+
1989
+ #: ../views/scans.php:461 ../views/scans-results.php:840
1990
+ msgid "Delete"
1991
+ msgstr ""
1992
+
1993
+ #: ../views/scans.php:498
1994
+ msgid "Do you want to remove these scans?"
1995
+ msgstr ""
1996
+
1997
+ #: ../views/settings.php:29
1998
+ msgid "Crawling"
1999
+ msgstr ""
2000
+
2001
+ #: ../views/settings.php:30
2002
+ msgid "Timing"
2003
+ msgstr ""
2004
+
2005
+ #: ../views/settings.php:31 ../views/scans-edit.php:93
2006
+ msgid "Advanced"
2007
+ msgstr ""
2008
+
2009
+ #: ../views/settings.php:40
2010
+ msgid "Number of crawler threads"
2011
+ msgstr ""
2012
+
2013
+ #: ../views/settings.php:42
2014
+ msgid ""
2015
+ "One thread means an HTTP request to your site only used for crawling "
2016
+ "purposes."
2017
+ msgstr ""
2018
+
2019
+ #: ../views/settings.php:45
2020
+ msgid "Max crawlers running"
2021
+ msgstr ""
2022
+
2023
+ #: ../views/settings.php:47
2024
+ msgid ""
2025
+ "Number of crawlers allowed to run simultaneously, each one with its own "
2026
+ "threads."
2027
+ msgstr ""
2028
+
2029
+ #: ../views/settings.php:50
2030
+ msgid "Max pack items"
2031
+ msgstr ""
2032
+
2033
+ #: ../views/settings.php:52
2034
+ msgid ""
2035
+ "Total objects (posts, comments or blogroll) processed in one single thread."
2036
+ msgstr ""
2037
+
2038
+ #: ../views/settings.php:55
2039
+ msgid "Max URL request attempts"
2040
+ msgstr ""
2041
+
2042
+ #: ../views/settings.php:57
2043
+ msgid "Number of HTTP requests attempts before set an URL as wrong."
2044
+ msgstr ""
2045
+
2046
+ #: ../views/settings.php:60
2047
+ msgid "Max redirections allowed"
2048
+ msgstr ""
2049
+
2050
+ #: ../views/settings.php:62
2051
+ msgid "Total redirections steps allowed to follow from original URL."
2052
+ msgstr ""
2053
+
2054
+ #: ../views/settings.php:65
2055
+ msgid "Max download size"
2056
+ msgstr ""
2057
+
2058
+ #: ../views/settings.php:67
2059
+ msgid "KB"
2060
+ msgstr ""
2061
+
2062
+ #: ../views/settings.php:67
2063
+ #, php-format
2064
+ msgid "(minimum value of %s KB and max value of %s KB)."
2065
+ msgstr ""
2066
+
2067
+ #: ../views/settings.php:70
2068
+ msgid "Default User Agent"
2069
+ msgstr ""
2070
+
2071
+ #: ../views/settings.php:81
2072
+ msgid "URL Connection timeout"
2073
+ msgstr ""
2074
+
2075
+ #: ../views/settings.php:83 ../views/settings.php:88 ../views/settings.php:93
2076
+ #: ../views/settings.php:98 ../views/settings.php:103
2077
+ #: ../views/settings.php:108 ../views/settings.php:113
2078
+ #: ../views/settings.php:118 ../views/scans-edit.php:516
2079
+ #: ../views/scans-edit.php:520 ../views/scans-edit.php:535
2080
+ #: ../views/scans-edit.php:539
2081
+ msgid "seconds"
2082
+ msgstr ""
2083
+
2084
+ #: ../views/settings.php:83
2085
+ msgid "Max time allowed to establish a connection with the URL host."
2086
+ msgstr ""
2087
+
2088
+ #: ../views/settings.php:86
2089
+ msgid "URL Request timeout"
2090
+ msgstr ""
2091
+
2092
+ #: ../views/settings.php:88
2093
+ msgid "Max time allowed to retrieve headers and body from one URL."
2094
+ msgstr ""
2095
+
2096
+ #: ../views/settings.php:91
2097
+ msgid "URL Extra timeout check"
2098
+ msgstr ""
2099
+
2100
+ #: ../views/settings.php:93 ../views/settings.php:98 ../views/settings.php:103
2101
+ #: ../views/settings.php:108 ../views/settings.php:113
2102
+ #: ../views/settings.php:118
2103
+ #, php-format
2104
+ msgid "(mininum value of %d seconds)"
2105
+ msgstr ""
2106
+
2107
+ #: ../views/settings.php:93
2108
+ msgid "A little grace period to avoid timeouts conflicts."
2109
+ msgstr ""
2110
+
2111
+ #: ../views/settings.php:96
2112
+ msgid "Check crawler alive each"
2113
+ msgstr ""
2114
+
2115
+ #: ../views/settings.php:98
2116
+ msgid "Checks if a crawler is interrupted and if so restart it."
2117
+ msgstr ""
2118
+
2119
+ #: ../views/settings.php:101
2120
+ msgid "Total objects check each"
2121
+ msgstr ""
2122
+
2123
+ #: ../views/settings.php:103
2124
+ msgid "Total of objects (posts, comments or blogroll) to check links."
2125
+ msgstr ""
2126
+
2127
+ #: ../views/settings.php:106
2128
+ msgid "Summary of status each"
2129
+ msgstr ""
2130
+
2131
+ #: ../views/settings.php:108
2132
+ msgid "Calculate status code totals to display data in real time."
2133
+ msgstr ""
2134
+
2135
+ #: ../views/settings.php:111
2136
+ msgid "Summary of URLs each"
2137
+ msgstr ""
2138
+
2139
+ #: ../views/settings.php:113
2140
+ msgid "Current number of URLs processed or waiting to be checked."
2141
+ msgstr ""
2142
+
2143
+ #: ../views/settings.php:116
2144
+ msgid "Summary of objects each"
2145
+ msgstr ""
2146
+
2147
+ #: ../views/settings.php:118
2148
+ msgid "Summary of objects (posts, comments or blogroll) with processed URLs."
2149
+ msgstr ""
2150
+
2151
+ #: ../views/settings.php:129
2152
+ msgid "Recursion limit"
2153
+ msgstr ""
2154
+
2155
+ #: ../views/settings.php:130
2156
+ msgid "function calls"
2157
+ msgstr ""
2158
+
2159
+ #: ../views/settings.php:133
2160
+ msgid "Data results pagination"
2161
+ msgstr ""
2162
+
2163
+ #: ../views/settings.php:134
2164
+ msgid "Use <code>SQL_CALC_FOUND_ROWS</code> to calculate total rows"
2165
+ msgstr ""
2166
+
2167
+ #: ../views/settings.php:141
2168
+ msgid "Save settings"
2169
+ msgstr ""
2170
+
2171
+ #: ../views/scans-results.php:112
2172
+ msgid "Status"
2173
+ msgstr ""
2174
+
2175
+ #: ../views/scans-results.php:114
2176
+ msgid "Content"
2177
+ msgstr ""
2178
+
2179
+ #: ../views/scans-results.php:213 ../views/scans-results.php:236
2180
+ #: ../core-pro/results.php:1876 ../core-pro/results.php:1889
2181
+ msgid "Error code "
2182
+ msgstr ""
2183
+
2184
+ #: ../views/scans-results.php:217 ../views/scans-results.php:240
2185
+ #, php-format
2186
+ msgid "error code %d"
2187
+ msgstr ""
2188
+
2189
+ #: ../views/scans-results.php:270
2190
+ msgid "Protocol relative"
2191
+ msgstr ""
2192
+
2193
+ #: ../views/scans-results.php:271
2194
+ msgid "Ignored"
2195
+ msgstr ""
2196
+
2197
+ #: ../views/scans-results.php:300 ../views/scans-results.php:316
2198
+ #: ../views/scans-results.php:1023 ../views/scans-results.php:1114
2199
+ #: ../core-pro/tools-url.php:644
2200
+ msgid "Request error"
2201
+ msgstr ""
2202
+
2203
+ #: ../views/scans-results.php:341
2204
+ msgid "Image"
2205
+ msgstr ""
2206
+
2207
+ #: ../views/scans-results.php:380
2208
+ #, php-format
2209
+ msgid "Edit &#8220;%s&#8221;"
2210
+ msgstr ""
2211
+
2212
+ #: ../views/scans-results.php:393
2213
+ #, php-format
2214
+ msgid "Entry %d not found"
2215
+ msgstr ""
2216
+
2217
+ #: ../views/scans-results.php:442
2218
+ #, php-format
2219
+ msgid "Comment %d not found"
2220
+ msgstr ""
2221
+
2222
+ #: ../views/scans-results.php:482
2223
+ #, php-format
2224
+ msgid "Bookmark %d not found"
2225
+ msgstr ""
2226
+
2227
+ #: ../views/scans-results.php:692
2228
+ msgid "Edit this item"
2229
+ msgstr ""
2230
+
2231
+ #: ../views/scans-results.php:692 ../views/scans-results.php:803
2232
+ #: ../views/scans-results.php:839
2233
+ msgid "Edit"
2234
+ msgstr ""
2235
+
2236
+ #: ../views/scans-results.php:699
2237
+ msgid "Restore this item from the Trash"
2238
+ msgstr ""
2239
+
2240
+ #: ../views/scans-results.php:699 ../views/scans-results.php:793
2241
+ msgid "Restore"
2242
+ msgstr ""
2243
+
2244
+ #: ../views/scans-results.php:703
2245
+ msgid "Move this item to the Trash"
2246
+ msgstr ""
2247
+
2248
+ #: ../views/scans-results.php:703 ../views/scans-results.php:799
2249
+ msgid "Trash"
2250
+ msgstr ""
2251
+
2252
+ #: ../views/scans-results.php:707
2253
+ msgid "Delete this item permanently"
2254
+ msgstr ""
2255
+
2256
+ #: ../views/scans-results.php:707 ../views/scans-results.php:797
2257
+ msgid "Delete Permanently"
2258
+ msgstr ""
2259
+
2260
+ #: ../views/scans-results.php:722
2261
+ #, php-format
2262
+ msgid "Preview &#8220;%s&#8221;"
2263
+ msgstr ""
2264
+
2265
+ #: ../views/scans-results.php:722
2266
+ msgid "Preview"
2267
+ msgstr ""
2268
+
2269
+ #: ../views/scans-results.php:727
2270
+ #, php-format
2271
+ msgid "View &#8220;%s&#8221;"
2272
+ msgstr ""
2273
+
2274
+ #: ../views/scans-results.php:727 ../views/scans-results.php:808
2275
+ msgid "View"
2276
+ msgstr ""
2277
+
2278
+ #: ../views/scans-results.php:781
2279
+ msgid "Unapprove this comment"
2280
+ msgstr ""
2281
+
2282
+ #: ../views/scans-results.php:781
2283
+ msgid "Unapprove"
2284
+ msgstr ""
2285
+
2286
+ #: ../views/scans-results.php:783
2287
+ msgid "Approve this comment"
2288
+ msgstr ""
2289
+
2290
+ #: ../views/scans-results.php:783
2291
+ msgid "Approve"
2292
+ msgstr ""
2293
+
2294
+ #: ../views/scans-results.php:787
2295
+ msgid "Mark this comment as spam"
2296
+ msgstr ""
2297
+
2298
+ #: ../views/scans-results.php:787
2299
+ msgid "Spam"
2300
+ msgstr ""
2301
+
2302
+ #: ../views/scans-results.php:789
2303
+ msgid "Not Spam"
2304
+ msgstr ""
2305
+
2306
+ #: ../views/scans-results.php:799
2307
+ msgid "Move this comment to the trash"
2308
+ msgstr ""
2309
+
2310
+ #: ../views/scans-results.php:803
2311
+ msgid "Edit comment"
2312
+ msgstr ""
2313
+
2314
+ #: ../views/scans-results.php:910
2315
+ msgid "Please, confirm you want to remove this entry pressing the Ok button"
2316
+ msgstr ""
2317
+
2318
+ #: ../views/scans-results.php:910
2319
+ msgid "Please, confirm you want to remove this comment pressing the Ok button"
2320
+ msgstr ""
2321
+
2322
+ #: ../views/scans-results.php:910
2323
+ msgid "Please, confirm you want to remove this bookmark pressing the Ok button"
2324
+ msgstr ""
2325
+
2326
+ #: ../views/scans-results.php:910
2327
+ msgid "Please, select any result to proceed"
2328
+ msgstr ""
2329
+
2330
+ #: ../views/scans-results.php:910
2331
+ msgid "error code"
2332
+ msgstr ""
2333
+
2334
+ #: ../views/scans-results.php:1019
2335
+ msgid "All results"
2336
+ msgstr ""
2337
+
2338
+ #: ../views/scans-results.php:1030
2339
+ msgid "(counters in progress)"
2340
+ msgstr ""
2341
+
2342
+ #: ../views/scans-results.php:1053
2343
+ msgid "Filter"
2344
+ msgstr ""
2345
+
2346
+ #: ../views/scans-results.php:1130
2347
+ msgid "All status codes"
2348
+ msgstr ""
2349
+
2350
+ #: ../views/scans-results.php:1160
2351
+ msgid "All content"
2352
+ msgstr ""
2353
+
2354
+ #: ../views/scans-results.php:1175
2355
+ msgid "All link types"
2356
+ msgstr ""
2357
+
2358
+ #: ../views/scans-edit.php:57
2359
+ #, php-format
2360
+ msgid ""
2361
+ "The crawler for this scan is <strong>stopped</strong> but you can see the <a "
2362
+ "href=\"%s\">crawling results</a> collected data."
2363
+ msgstr ""
2364
+
2365
+ #: ../views/scans-edit.php:57
2366
+ #, php-format
2367
+ msgid "Or you can <a href=\"%s\">start again the crawler</a>."
2368
+ msgstr ""
2369
+
2370
+ #: ../views/scans-edit.php:63
2371
+ #, php-format
2372
+ msgid ""
2373
+ "The crawler for this scan is <strong>running</strong> and you can see its <a "
2374
+ "href=\"%s\">crawling results data</a>."
2375
+ msgstr ""
2376
+
2377
+ #: ../views/scans-edit.php:63
2378
+ #, php-format
2379
+ msgid "If needed you can <a href=\"%s\">stop the crawler</a>."
2380
+ msgstr ""
2381
+
2382
+ #: ../views/scans-edit.php:69
2383
+ #, php-format
2384
+ msgid ""
2385
+ "The crawler has ended and you can see all its <a href=\"%s\">crawling "
2386
+ "results</a>."
2387
+ msgstr ""
2388
+
2389
+ #: ../views/scans-edit.php:88
2390
+ msgid "General"
2391
+ msgstr ""
2392
+
2393
+ #: ../views/scans-edit.php:89
2394
+ msgid "Content options"
2395
+ msgstr ""
2396
+
2397
+ #: ../views/scans-edit.php:90
2398
+ msgid "Content filters"
2399
+ msgstr ""
2400
+
2401
+ #: ../views/scans-edit.php:91
2402
+ msgid "Links status"
2403
+ msgstr ""
2404
+
2405
+ #: ../views/scans-edit.php:92
2406
+ msgid "Schedule"
2407
+ msgstr ""
2408
+
2409
+ #: ../views/scans-edit.php:103
2410
+ msgid "Scan name"
2411
+ msgstr ""
2412
+
2413
+ #: ../views/scans-edit.php:104
2414
+ msgid "optional"
2415
+ msgstr ""
2416
+
2417
+ #: ../views/scans-edit.php:113 ../views/scans-edit.php:120
2418
+ msgid "Link types"
2419
+ msgstr ""
2420
+
2421
+ #: ../views/scans-edit.php:134 ../views/scans-edit.php:141
2422
+ msgid "Destination type"
2423
+ msgstr ""
2424
+
2425
+ #: ../views/scans-edit.php:153 ../views/scans-edit.php:160
2426
+ msgid "Time scope"
2427
+ msgstr ""
2428
+
2429
+ #: ../views/scans-edit.php:170 ../views/scans-edit.php:177
2430
+ msgid "Crawl order"
2431
+ msgstr ""
2432
+
2433
+ #: ../views/scans-edit.php:187 ../views/scans-edit.php:194
2434
+ msgid "Redirection status"
2435
+ msgstr ""
2436
+
2437
+ #: ../views/scans-edit.php:188
2438
+ msgid "Check status of destination URLs"
2439
+ msgstr ""
2440
+
2441
+ #: ../views/scans-edit.php:195
2442
+ msgid "<strong>Yes</strong>, check status of destination URLs"
2443
+ msgstr ""
2444
+
2445
+ #: ../views/scans-edit.php:195
2446
+ msgid "No check status of destination URLs"
2447
+ msgstr ""
2448
+
2449
+ #: ../views/scans-edit.php:204 ../views/scans-edit.php:211
2450
+ msgid "Malformed URLs"
2451
+ msgstr ""
2452
+
2453
+ #: ../views/scans-edit.php:205
2454
+ msgid "Track malformed links"
2455
+ msgstr ""
2456
+
2457
+ #: ../views/scans-edit.php:212
2458
+ msgid "<strong>Yes</strong>, track malformed links"
2459
+ msgstr ""
2460
+
2461
+ #: ../views/scans-edit.php:212
2462
+ msgid "No tracking of malformed links"
2463
+ msgstr ""
2464
+
2465
+ #: ../views/scans-edit.php:221
2466
+ msgid "Notifications"
2467
+ msgstr ""
2468
+
2469
+ #: ../views/scans-edit.php:222
2470
+ msgid "Send an e-mail when the scan is completed:"
2471
+ msgstr ""
2472
+
2473
+ #: ../views/scans-edit.php:223
2474
+ #, php-format
2475
+ msgid "Send to the current blog address <strong>%s</strong>"
2476
+ msgstr ""
2477
+
2478
+ #: ../views/scans-edit.php:224
2479
+ msgid "Send to these e-mail addresses:"
2480
+ msgstr ""
2481
+
2482
+ #: ../views/scans-edit.php:284 ../views/scans-edit.php:292
2483
+ msgid "Custom fields"
2484
+ msgstr ""
2485
+
2486
+ #: ../views/scans-edit.php:286
2487
+ msgid "Custom field key"
2488
+ msgstr ""
2489
+
2490
+ #: ../views/scans-edit.php:286 ../views/scans-edit.php:345
2491
+ #: ../views/scans-edit.php:368 ../views/scans-edit.php:387
2492
+ #: ../views/scans-edit.php:413
2493
+ msgid "Add"
2494
+ msgstr ""
2495
+
2496
+ #: ../views/scans-edit.php:301 ../views/scans-edit.php:308
2497
+ msgid "Comment links"
2498
+ msgstr ""
2499
+
2500
+ #: ../views/scans-edit.php:317 ../views/scans-edit.php:324
2501
+ msgid "Also check links in"
2502
+ msgstr ""
2503
+
2504
+ #: ../views/scans-edit.php:318 ../views/scans-edit.php:325
2505
+ msgid "Blogroll links"
2506
+ msgstr ""
2507
+
2508
+ #: ../views/scans-edit.php:341 ../views/scans-edit.php:351
2509
+ msgid "Anchor filters"
2510
+ msgstr ""
2511
+
2512
+ #: ../views/scans-edit.php:344
2513
+ msgid "Anchor text filter"
2514
+ msgstr ""
2515
+
2516
+ #: ../views/scans-edit.php:364 ../views/scans-edit.php:374
2517
+ msgid "Include URLs"
2518
+ msgstr ""
2519
+
2520
+ #: ../views/scans-edit.php:383 ../views/scans-edit.php:393
2521
+ msgid "Exclude URLs"
2522
+ msgstr ""
2523
+
2524
+ #: ../views/scans-edit.php:406 ../views/scans-edit.php:419
2525
+ msgid "HTML attributes"
2526
+ msgstr ""
2527
+
2528
+ #: ../views/scans-edit.php:410
2529
+ msgid "Attribute name"
2530
+ msgstr ""
2531
+
2532
+ #: ../views/scans-edit.php:412
2533
+ msgid "Attribute value"
2534
+ msgstr ""
2535
+
2536
+ #: ../views/scans-edit.php:429
2537
+ msgid "Accelerate crawling process integrating filters in main database query"
2538
+ msgstr ""
2539
+
2540
+ #: ../views/scans-edit.php:437
2541
+ msgid "Yes"
2542
+ msgstr ""
2543
+
2544
+ #: ../views/scans-edit.php:437
2545
+ msgid "No"
2546
+ msgstr ""
2547
+
2548
+ #: ../views/scans-edit.php:453 ../views/scans-edit.php:460
2549
+ msgid "Track links by level"
2550
+ msgstr ""
2551
+
2552
+ #: ../views/scans-edit.php:469 ../views/scans-edit.php:489
2553
+ msgid "Track links by code"
2554
+ msgstr ""
2555
+
2556
+ #: ../views/scans-edit.php:511 ../views/scans-edit.php:530
2557
+ msgid "Number of threads"
2558
+ msgstr ""
2559
+
2560
+ #: ../views/scans-edit.php:515 ../views/scans-edit.php:534
2561
+ msgid "Connection timeout"
2562
+ msgstr ""
2563
+
2564
+ #: ../views/scans-edit.php:519 ../views/scans-edit.php:538
2565
+ msgid "Request timeout"
2566
+ msgstr ""
2567
+
2568
+ #: ../views/scans-edit.php:526
2569
+ msgid "All these values are optional, leave them empty to use plugin defaults."
2570
+ msgstr ""
2571
+
2572
+ #: ../views/scans-edit.php:531
2573
+ #, php-format
2574
+ msgid "(optional, default %d threads)"
2575
+ msgstr ""
2576
+
2577
+ #: ../views/scans-edit.php:535 ../views/scans-edit.php:539
2578
+ #, php-format
2579
+ msgid "(optional, default %d seconds)"
2580
+ msgstr ""
2581
+
2582
+ #: ../views/scans-edit.php:548
2583
+ msgid "Save scan changes"
2584
+ msgstr ""
2585
+
2586
+ #: ../views/scans-edit.php:549
2587
+ msgid "Save and run crawler"
2588
+ msgstr ""
2589
+
2590
+ #: ../views/scans-edit.php:550
2591
+ msgid "Delete this scan"
2592
+ msgstr ""
2593
+
2594
+ #: ../core-pro/tools-url.php:60
2595
+ msgid "Empty URLs pack"
2596
+ msgstr ""
2597
+
2598
+ #: ../core-pro/tools-url.php:67
2599
+ msgid "Error when process URLs pack"
2600
+ msgstr ""
2601
+
2602
+ #: ../core-pro/tools-url.php:219
2603
+ msgid "Removed object element"
2604
+ msgstr ""
2605
+
2606
+ #: ../core-pro/tools-url.php:245
2607
+ msgid "nofollow exists"
2608
+ msgstr ""
2609
+
2610
+ #: ../core-pro/tools-url.php:260
2611
+ msgid "Changed to nofollow"
2612
+ msgstr ""
2613
+
2614
+ #: ../core-pro/tools-url.php:272
2615
+ msgid "Set nofollow previously"
2616
+ msgstr ""
2617
+
2618
+ #: ../core-pro/tools-url.php:282
2619
+ msgid "nofollow not exists"
2620
+ msgstr ""
2621
+
2622
+ #: ../core-pro/tools-url.php:298
2623
+ msgid "Removed nofollow"
2624
+ msgstr ""
2625
+
2626
+ #: ../core-pro/tools-url.php:310
2627
+ msgid "Removed nofollow previously"
2628
+ msgstr ""
2629
+
2630
+ #: ../core-pro/tools-url.php:320
2631
+ msgid "Removed link"
2632
+ msgstr ""
2633
+
2634
+ #: ../core-pro/tools-url.php:328
2635
+ msgid "Removed link before"
2636
+ msgstr ""
2637
+
2638
+ #: ../core-pro/tools-url.php:341
2639
+ msgid "Something went wrong"
2640
+ msgstr ""
2641
+
2642
+ #: ../core-pro/tools-url.php:379
2643
+ msgid "Changed redirection location"
2644
+ msgstr ""
2645
+
2646
+ #: ../core-pro/tools-url.php:390
2647
+ msgid "Redirection changed previously"
2648
+ msgstr ""
2649
+
2650
+ #: ../core-pro/tools-url.php:426
2651
+ msgid "Database Match"
2652
+ msgstr ""
2653
+
2654
+ #: ../core-pro/tools-url.php:588
2655
+ msgid "Malformed URL"
2656
+ msgstr ""
2657
+
2658
+ #: ../core-pro/tools-url.php:622
2659
+ msgid "Empty request response"
2660
+ msgstr ""
2661
+
2662
+ #: ../core-pro/tools-url.php:630
2663
+ msgid "Empty response data"
2664
+ msgstr ""
2665
+
2666
+ #: ../core-pro/tools-url.php:640
2667
+ msgid "Malformed response data"
2668
+ msgstr ""
2669
+
2670
+ #: ../core-pro/tools-url.php:648
2671
+ msgid "Missing response data"
2672
+ msgstr ""
2673
+
2674
+ #: ../core-pro/tools-url.php:661
2675
+ msgid "Redirect header but missing Location"
2676
+ msgstr ""
2677
+
2678
+ #: ../core-pro/tools-url.php:671
2679
+ msgid "Redirect header and Location but bad URL"
2680
+ msgstr ""
2681
+
2682
+ #: ../core-pro/tools-url.php:704
2683
+ msgid "No 301 redirect header"
2684
+ msgstr ""
2685
+
2686
+ #: ../core-pro/results.php:116
2687
+ msgid "Missing or invalid action parameter"
2688
+ msgstr ""
2689
+
2690
+ #: ../core-pro/results.php:127
2691
+ msgid "Missing or invalid result identifier parameter"
2692
+ msgstr ""
2693
+
2694
+ #: ../core-pro/results.php:137 ../core-pro/results.php:150
2695
+ msgid "Resource not found in database"
2696
+ msgstr ""
2697
+
2698
+ #: ../core-pro/results.php:158
2699
+ msgid "Unable to retrieve the resource associated scan"
2700
+ msgstr ""
2701
+
2702
+ #: ../core-pro/results.php:321
2703
+ msgid "Headers information not found"
2704
+ msgstr ""
2705
+
2706
+ #: ../core-pro/results.php:869 ../core-pro/results.php:1519
2707
+ msgid "Link already unlinked"
2708
+ msgstr ""
2709
+
2710
+ #: ../core-pro/results.php:876
2711
+ msgid "Link type unknown"
2712
+ msgstr ""
2713
+
2714
+ #: ../core-pro/results.php:883
2715
+ msgid "Input value missing"
2716
+ msgstr ""
2717
+
2718
+ #: ../core-pro/results.php:890
2719
+ msgid "Operation not allowed"
2720
+ msgstr ""
2721
+
2722
+ #: ../core-pro/results.php:897
2723
+ msgid "Object not found"
2724
+ msgstr ""
2725
+
2726
+ #: ../core-pro/results.php:904
2727
+ msgid "Not valid URL"
2728
+ msgstr ""
2729
+
2730
+ #: ../core-pro/results.php:917
2731
+ msgid "Cannot retrieve element content"
2732
+ msgstr ""
2733
+
2734
+ #: ../core-pro/results.php:931
2735
+ msgid "Not valid context"
2736
+ msgstr ""
2737
+
2738
+ #: ../core-pro/results.php:960
2739
+ msgid "Unable to parse the current link"
2740
+ msgstr ""
2741
+
2742
+ #: ../core-pro/results.php:967
2743
+ msgid "Saved URL does not match the link URL"
2744
+ msgstr ""
2745
+
2746
+ #: ../core-pro/results.php:974
2747
+ msgid "Saved anchor does not match the anchor link"
2748
+ msgstr ""
2749
+
2750
+ #: ../core-pro/results.php:996 ../core-pro/results.php:1353
2751
+ #: ../core-pro/results.php:1420
2752
+ msgid "No anchor text changes detected."
2753
+ msgstr ""
2754
+
2755
+ #: ../core-pro/results.php:1007 ../core-pro/results.php:1054
2756
+ #: ../core-pro/results.php:1095 ../core-pro/results.php:1167
2757
+ #: ../core-pro/results.php:1199
2758
+ msgid "Cannot update, stored content does not match."
2759
+ msgstr ""
2760
+
2761
+ #: ../core-pro/results.php:1124
2762
+ msgid "Unable to parse the current image"
2763
+ msgstr ""
2764
+
2765
+ #: ../core-pro/results.php:1131
2766
+ msgid "Saved URL does not match the image URL"
2767
+ msgstr ""
2768
+
2769
+ #: ../core-pro/results.php:1160 ../core-pro/results.php:1284
2770
+ #: ../core-pro/results.php:1322 ../core-pro/results.php:1389
2771
+ msgid "No URL changes detected."
2772
+ msgstr ""
2773
+
2774
+ #: ../core-pro/results.php:1174 ../core-pro/results.php:1294
2775
+ #: ../core-pro/results.php:1332 ../core-pro/results.php:1399
2776
+ msgid "Failed to check the new link."
2777
+ msgstr ""
2778
+
2779
+ #: ../core-pro/results.php:1274
2780
+ msgid "Failed to retrieve post meta data."
2781
+ msgstr ""
2782
+
2783
+ #: ../core-pro/results.php:1526 ../core-pro/results.php:1564
2784
+ msgid "Given URL missing."
2785
+ msgstr ""
2786
+
2787
+ #: ../core-pro/results.php:1571
2788
+ msgid "No URL status info available."
2789
+ msgstr ""
2790
+
2791
+ #: ../core-pro/results.php:1578
2792
+ msgid "No allowed redirections."
2793
+ msgstr ""
2794
+
2795
+ #: ../core-pro/results.php:1585
2796
+ msgid "No redirection found."
2797
+ msgstr ""
2798
+
2799
+ #: ../core-pro/results.php:1984
2800
+ msgid "Post not found"
2801
+ msgstr ""
2802
+
2803
+ #: ../core-pro/results.php:1994
2804
+ msgid "Current user can`t edit this post"
2805
+ msgstr ""
2806
+
2807
+ #: ../core-pro/results.php:2014
2808
+ msgid "Comment not found"
2809
+ msgstr ""
2810
+
2811
+ #: ../core-pro/results.php:2024
2812
+ msgid "Current user can`t edit this comment"
2813
+ msgstr ""
2814
+
2815
+ #: ../core-pro/results.php:2044
2816
+ msgid "Bookmark record not found"
2817
+ msgstr ""
2818
+
2819
+ #: ../core-pro/results.php:2054
2820
+ msgid "Current user can`t edit this bookmark"
2821
+ msgstr ""
readme.txt ADDED
@@ -0,0 +1,79 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === WP Broken Link Status Checker ===
2
+ Contributors: seedplugins, pauiglesias
3
+ Tags: broken links, broken, links, crawler, headers, http, nofollow, redirections, scan, status, checker, url
4
+ Requires at least: 3.4
5
+ Tested up to: 4.4.2
6
+ Stable tag: 1.0
7
+ License: GPLv2 or later
8
+
9
+ Check HTTP status response codes of all your content links and images, looking for broken links, redirections, nofollow links, etc.
10
+
11
+ == Description ==
12
+
13
+ This plugin is a broken link checker utility organized through entities called scans, each one containing its own configuration and results.
14
+
15
+ Start creating a new scan, and once a scan is configured you can start the crawler from the same edit page, or run it later from the scans list screen.
16
+
17
+ Knowing that these crawling processes can hurt your server perfomance, we have tried to put the focus on performance impacts, without performing massive data queries or updates, and not prioritizing crawler activity ahead of real visits.
18
+
19
+ Once started, you can see results inmediately without having to wait for the scan to be completed. You can access to the results page doing a click in the scan name, or clicking the "Show results" link from the scan actions row.
20
+
21
+ The crawler results page shows all links detected according to the scan configuration, allowing basic filtering options.
22
+
23
+ You can read a detailed user guide and the documentation from the plugin page:
24
+
25
+ http://seedplugins.com/wp-link-status/
26
+
27
+ == Installation ==
28
+
29
+ Install from WordPress
30
+
31
+ 1. Visit the Plugins page from your WordPress main menu and select Add New link
32
+ 1. Click on Upload plugin, next Browse, choose wp-link-status.zip and press Install Now.
33
+ 1. Once uploaded and install click Activate Plugin
34
+ 1. Get started from the WP Link Status menu
35
+
36
+ Or upload via FTP
37
+
38
+ 1. Unzip and upload wp-link-status folder to the `/wp-content/plugins/` directory
39
+ 1. Activate the plugin through the 'Plugins' menu in WordPress
40
+ 1. Get started from the WP Link Status menu
41
+
42
+ == Frequently Asked Questions ==
43
+
44
+ = Do I need to keep the browser opened, or a WordPress user session active during the crawling process? =
45
+
46
+ No, it is not necessary. The crawler module runs in background and it is intended to work in unattended mode.
47
+
48
+ = How the crawling process affects to server performance? =
49
+
50
+ There are several mechanisms to avoid constant database access. When a URL is found, the crawler stops extracting URLs from content and check the detected URL. In addition, in the settings page you can manage many parameters related to performance. Let me known if you experience any issue.
51
+
52
+ = Why the crawler does not work and show "Waiting..."? =
53
+
54
+ The crawler module works submitting HTTP requests through internal plugin scripts. So if you are running this plugin under an environment outside the Internet (e.g. local or development server) you need to add the involved hosts names or domains into your hosts file, both the client and server where this plugin is executed.
55
+
56
+ In the same way, if the site you are crawling implements browser password protection, you need to remove this password restriction in order to work properly.
57
+
58
+ == Screenshots ==
59
+
60
+ 1. Scan basic configuration
61
+ 1. Scan content options
62
+ 1. Scan URL filters
63
+ 1. Scan HTTP status selection
64
+ 1. Scan advanced options
65
+ 1. Main scans list
66
+ 1. Crawler results page
67
+
68
+ == Changelog ==
69
+
70
+ = 1.0 =
71
+ Release Date: February 11th, 2016
72
+
73
+ * First and tested released until WordPress 4.4.2
74
+ * Tested code from WordPress 3.4 version.
75
+
76
+ == Upgrade Notice ==
77
+
78
+ = 1.0 =
79
+ Initial Release.
views/extensions.php ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Load views class
4
+ require_once(dirname(__FILE__).'/views.php');
5
+
6
+ /**
7
+ * WP Link Status Views Extensions class
8
+ *
9
+ * @package WP Link Status
10
+ * @subpackage WP Link Status Views
11
+ */
12
+ class WPLNST_Views_Extensions extends WPLNST_Views {
13
+
14
+
15
+
16
+ /**
17
+ * Show scan edit form
18
+ */
19
+ public static function view($args) {
20
+
21
+ // Vars
22
+ extract($args);
23
+
24
+ // Plugin images
25
+ $base_url = plugins_url('assets/images/extensions/', WPLNST_FILE);
26
+
27
+ ?><div id="wplnst-extensions">
28
+
29
+
30
+ <div class="wplnst-extensions-section">
31
+
32
+ <p><a href="http://seedplugins.com/wp-link-status/" target="_blank"><img src="<?php echo $base_url.'pro-banner.jpg'; ?>" width="590" height="300" border="0" /></a></p>
33
+
34
+ <p style="width: 590px; text-align: center;"><a href="http://seedplugins.com/wp-link-status/" target="_blank" class="button-primary">Get WP Link Status Pro version</a></p>
35
+
36
+ </div>
37
+
38
+
39
+ <div class="wplnst-extensions-section">
40
+
41
+ <h3>Advanced filters</h3>
42
+
43
+ <p><img src="<?php echo $base_url.'wpls-web-crawler-results-advanced.png'; ?>" width="800" height="120" border="0" /></p>
44
+
45
+ </div>
46
+
47
+
48
+ <div class="wplnst-extensions-section">
49
+
50
+ <h3>Inline result editing</h3>
51
+
52
+ <p><img src="<?php echo $base_url.'wpls-web-crawler-results-actions.png'; ?>" width="800" height="100" border="0" /></p>
53
+
54
+ <p><img src="<?php echo $base_url.'wpls-web-crawler-edit.png'; ?>" width="800" height="300" border="0" /></p>
55
+
56
+ </div>
57
+
58
+
59
+ <div class="wplnst-extensions-section">
60
+
61
+ <h3>Extra URL Tools</h3>
62
+
63
+ <p><img src="<?php echo $base_url.'wpls-web-crawler-url-tools.png'; ?>" width="800" height="363" border="0" /></p>
64
+
65
+ <p><img src="<?php echo $base_url.'wpls-web-crawler-url-tools-results.png'; ?>" width="800" height="427" border="0" /></p>
66
+
67
+ </div>
68
+
69
+ <p style="width: 800px; text-align: center;"><a href="http://seedplugins.com/wp-link-status/" target="_blank" class="button-primary">Get WP Link Status Pro version</a></p>
70
+
71
+
72
+ </div><?php
73
+ }
74
+
75
+
76
+
77
+ }
views/scans-edit.php ADDED
@@ -0,0 +1,559 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Load views class
4
+ require_once(dirname(__FILE__).'/views.php');
5
+
6
+ /**
7
+ * WP Link Status Views Scans Edit class
8
+ *
9
+ * @package WP Link Status
10
+ * @subpackage WP Link Status Views
11
+ */
12
+ class WPLNST_Views_Scans_Edit extends WPLNST_Views {
13
+
14
+
15
+
16
+ /**
17
+ * Show scan edit form
18
+ */
19
+ public static function view($args) {
20
+
21
+ // Vars
22
+ extract($args);
23
+
24
+ // Initialize
25
+ $is_ready = true;
26
+ $editable = ('wait' == $scan->status);
27
+
28
+ // Check errors
29
+ $link_types_error = $post_types_error = $post_status_error = $link_status_error = false;
30
+ if (!empty($notice_ready) && is_array($notice_ready)) {
31
+
32
+ // Inside form errors
33
+ $link_types_error = isset($notice_ready['link_types']);
34
+ $post_types_error = isset($notice_ready['post_types']);
35
+ $post_status_error = isset($notice_ready['post_status']);
36
+ $link_status_error = isset($notice_ready['link_status']);
37
+
38
+ // Determine if ready
39
+ $is_ready = !($link_types_error || $post_types_error || $post_status_error || $link_status_error);
40
+
41
+ // Show notice errors
42
+ echo '<div class="error notice">';
43
+ foreach ($notice_ready as $notice_key => $notice_title)
44
+ echo '<p>'.$notice_title.'</p>';
45
+ echo '</div>';
46
+ } ?>
47
+
48
+ <?php if ($is_ready && $scan->id > 0) :
49
+
50
+ // Prepare URLs
51
+ $results_url = esc_url(WPLNST_Core_Plugin::get_url_scans_results($scan->id));
52
+ $start_url = esc_url(WPLNST_Core_Plugin::get_url_scans_crawler($scan->id, 'on', $scan->hash));
53
+ $stop_url = esc_url(WPLNST_Core_Plugin::get_url_scans_crawler($scan->id, 'off', $scan->hash)); ?>
54
+
55
+ <?php if ('stop' == $scan->status) : ?>
56
+
57
+ <div class="notice"><p><?php printf(__('The crawler for this scan is <strong>stopped</strong> but you can see the <a href="%s">crawling results</a> collected data.', 'wplnst'), $results_url); if ($more_scans) : echo ' '; printf(__('Or you can <a href="%s">start again the crawler</a>.', 'wplnst'), $start_url); endif; ?></p></div>
58
+
59
+ <?php elseif ('play' == $scan->status) : ?>
60
+
61
+ <?php if (empty($_POST['scan_run']) && empty($_GET['started'])) : ?>
62
+
63
+ <div class="notice"><p><?php printf(__('The crawler for this scan is <strong>running</strong> and you can see its <a href="%s">crawling results data</a>.', 'wplnst'), $results_url); echo ' '; printf(__('If needed you can <a href="%s">stop the crawler</a>.', 'wplnst'), $stop_url); ?></p></div>
64
+
65
+ <?php endif; ?>
66
+
67
+ <?php elseif ('end' == $scan->status) : ?>
68
+
69
+ <div class="notice"><p><?php printf(__('The crawler is completed and you can see all its <a href="%s">crawling results</a>.', 'wplnst'), $results_url); ?></p></div>
70
+
71
+ <?php endif; ?>
72
+
73
+ <?php endif; ?>
74
+
75
+ <form method="post" id="wplnst-form" action="<?php echo esc_url($action); ?>">
76
+
77
+ <input type="hidden" name="scan_id" id="wplnst-scan-id" value="<?php echo esc_attr($scan->id); ?>" />
78
+ <input type="hidden" name="scan_run" id="wplnst-scan-run" value="" />
79
+ <input type="hidden" name="scan_edit_nonce" value="<?php echo esc_attr($nonce); ?>" />
80
+
81
+ <input type="hidden" name="scan_custom_fields" id="wplnst-scan-custom-fields" value='<?php echo self::esc_attr_elist($scan->custom_fields); ?>' />
82
+ <input type="hidden" name="scan_anchor_filters" id="wplnst-scan-anchor-filters" value='<?php echo self::esc_attr_elist($scan->anchor_filters); ?>' />
83
+ <input type="hidden" name="scan_include_urls" id="wplnst-scan-include-urls" value='<?php echo self::esc_attr_elist($scan->include_urls); ?>' />
84
+ <input type="hidden" name="scan_exclude_urls" id="wplnst-scan-exclude-urls" value='<?php echo self::esc_attr_elist($scan->exclude_urls); ?>' />
85
+ <input type="hidden" name="scan_html_attributes" id="wplnst-scan-html-attributes" value='<?php echo self::esc_attr_elist($scan->html_attributes); ?>' />
86
+
87
+ <h2 id="wplnst-tabs-nav" class="nav-tab-wrapper">
88
+ <a id="wplnst-general-tab" href="#top#wplnst-general" class="nav-tab"<?php if ($link_types_error) echo ' style="color: red;"'; ?>><?php _e('General', 'wplnst'); ?></a>
89
+ <a id="wplnst-content-tab" href="#top#wplnst-content" class="nav-tab"<?php if ($post_types_error || $post_status_error) echo ' style="color: red;"'; ?>><?php _e('Content options', 'wplnst'); ?></a>
90
+ <a id="wplnst-filters-tab" href="#top#wplnst-filters" class="nav-tab"><?php _e('Content filters', 'wplnst'); ?></a>
91
+ <a id="wplnst-status-tab" href="#top#wplnst-status" class="nav-tab"<?php if ($link_status_error) echo ' style="color: red;"'; ?>><?php _e('Links status', 'wplnst'); ?></a>
92
+ <?php if (false) : ?><a id="wplnst-scheduled-tab" href="#top#wplnst-scheduled" class="nav-tab"><?php _e('Schedule', 'wplnst'); ?></a><?php endif; ?>
93
+ <a id="wplnst-advanced-tab" href="#top#wplnst-advanced" class="nav-tab"><?php _e('Advanced', 'wplnst'); ?></a>
94
+ </h2>
95
+
96
+ <div id="wplnst-tabs" class="wplnst-tabs-scan-edit">
97
+
98
+ <div id="wplnst-general" class="wplnst-tab wplnst-tab-default">
99
+
100
+ <table class="form-table">
101
+
102
+ <tr>
103
+ <th><label for="tx-name"><?php _e('Scan name', 'wplnst'); ?></label></th>
104
+ <td><input type="text" name="tx-name" id="tx-name" value="<?php echo esc_attr($scan->name); ?>" class="regular-text" maxlength="255" /> (<?php _e('optional', 'wplnst'); ?>)</td>
105
+ </tr>
106
+
107
+
108
+ <?php if (!empty($link_types)) : ?>
109
+
110
+ <?php if ($editable) : ?>
111
+
112
+ <tr>
113
+ <th<?php if ($link_types_error) echo ' style="color: red;"'; ?>><?php _e('Link types', 'wplnst'); ?></th>
114
+ <td class="wplnst-list"><?php foreach ($link_types as $key => $name) : ?><input <?php self::checked($key, $scan->link_types); ?> type="checkbox" name="ck-link-type[<?php echo $key; ?>]" id="ck-link-type-<?php echo $key; ?>" value="on" /><label for="ck-link-type-<?php echo $key; ?>"><?php echo $name; ?></label> &nbsp; <?php endforeach; ?></td>
115
+ </tr>
116
+
117
+ <?php else : ?>
118
+
119
+ <tr>
120
+ <th><?php _e('Link types', 'wplnst'); ?></th>
121
+ <td class="wplnst-value-list"><?php if (!empty($scan->link_types_names) && is_array($scan->link_types_names)) echo implode(', ', array_map('esc_html', $scan->link_types_names)); ?></td>
122
+ </tr>
123
+
124
+ <?php endif; ?>
125
+
126
+ <?php endif; ?>
127
+
128
+
129
+ <?php if (!empty($destination_types)) : ?>
130
+
131
+ <?php if ($editable) : ?>
132
+
133
+ <tr>
134
+ <th><label for="wplnst-destination-type"><?php _e('Destination type', 'wplnst'); ?></label></th>
135
+ <td><select id="wplnst-destination-type" name="sl-destination-type"><?php self::options($destination_types, $scan->destination_type); ?></select></td>
136
+ </tr>
137
+
138
+ <?php else : ?>
139
+
140
+ <tr>
141
+ <th><?php _e('Destination type', 'wplnst'); ?></th>
142
+ <td class="wplnst-value"><?php echo esc_html($scan->destination_type_name); ?></td>
143
+ </tr>
144
+
145
+ <?php endif; ?>
146
+
147
+ <?php endif; ?>
148
+
149
+
150
+ <?php if ($editable) : ?>
151
+
152
+ <tr>
153
+ <th><label for="wplnst-time-scope"><?php _e('Time scope', 'wplnst'); ?></label></th>
154
+ <td><select id="wplnst-time-scope" name="sl-time-scope"><?php self::options($time_scopes, $scan->time_scope); ?></select></td>
155
+ </tr>
156
+
157
+ <?php else : ?>
158
+
159
+ <tr>
160
+ <th><?php _e('Time scope', 'wplnst'); ?></th>
161
+ <td class="wplnst-value"><?php echo esc_html($scan->time_scope_name); ?></td>
162
+ </tr>
163
+
164
+ <?php endif; ?>
165
+
166
+
167
+ <?php if ($editable) : ?>
168
+
169
+ <tr>
170
+ <th><label for="wplnst-crawl-order"><?php _e('Crawl order', 'wplnst'); ?></label></th>
171
+ <td><select id="wplnst-crawl-order" name="sl-crawl-order"><?php self::options($crawl_order, $scan->crawl_order); ?></select></td>
172
+ </tr>
173
+
174
+ <?php else : ?>
175
+
176
+ <tr>
177
+ <th><?php _e('Crawl order', 'wplnst'); ?></th>
178
+ <td class="wplnst-value"><?php echo esc_html($scan->crawl_order_name); ?></td>
179
+ </tr>
180
+
181
+ <?php endif; ?>
182
+
183
+
184
+ <?php if ($editable) : ?>
185
+
186
+ <tr>
187
+ <th><?php _e('Redirection status', 'wplnst'); ?></th>
188
+ <td class="wplnst-list"><input <?php self::checked($scan->redir_status, true); ?> type="checkbox" id="ck-redir-status" name="ck-redir-status" value="on" /><label for="ck-redir-status"><?php _e('Check status of destination URLs', 'wplnst'); ?></label></td>
189
+ </tr>
190
+
191
+ <?php else : ?>
192
+
193
+ <tr>
194
+ <th><?php _e('Redirection status', 'wplnst'); ?></th>
195
+ <td class="wplnst-value"><?php echo $scan->redir_status? __('<strong>Yes</strong>, check status of destination URLs', 'wplnst') : __('No check status of destination URLs', 'wplnst'); ?></td>
196
+ </tr>
197
+
198
+ <?php endif; ?>
199
+
200
+
201
+ <?php if ($editable) : ?>
202
+
203
+ <tr>
204
+ <th><?php _e('Malformed URLs', 'wplnst'); ?></th>
205
+ <td class="wplnst-list"><input <?php self::checked($scan->malformed, true); ?> type="checkbox" id="ck-malformed-links" name="ck-malformed-links" value="on" /><label for="ck-malformed-links"><?php _e('Track malformed links', 'wplnst'); ?></label></td>
206
+ </tr>
207
+
208
+ <?php else : ?>
209
+
210
+ <tr>
211
+ <th><?php _e('Malformed URLs', 'wplnst'); ?></th>
212
+ <td class="wplnst-value"><?php echo $scan->malformed? __('<strong>Yes</strong>, track malformed links', 'wplnst') : __('No tracking of malformed links', 'wplnst'); ?></td>
213
+ </tr>
214
+
215
+ <?php endif; ?>
216
+
217
+
218
+ <?php if ('end' != $scan->status) : ?>
219
+
220
+ <tr>
221
+ <th><?php _e('Notifications', 'wplnst'); ?></th>
222
+ <td class="wplnst-list"><p><?php _e('Send an e-mail when the scan is completed:', 'wplnst'); ?></p>
223
+ <p><input <?php self::checked($scan->notify_default, true); ?> type="checkbox" id="ck-notify-default" name="ck-notify-default" value="on" /><label for="ck-notify-default"><?php printf(__('Send to the current blog address <strong>%s</strong>', 'wplnst'), get_option('admin_email'));; ?></label></p>
224
+ <p><input <?php self::checked($scan->notify_address, true); ?> type="checkbox" id="ck-notify-address" name="ck-notify-address" value="on" /><label for="ck-notify-address"><?php _e('Send to these e-mail addresses:', 'wplnst'); ?></label><br /><input type="text" name="tx-notify-address-email" id="tx-notify-address-email" value="<?php echo esc_attr($scan->notify_address_email); ?>" class="regular-text" maxlength="255" /></p></td>
225
+ </tr>
226
+
227
+ <?php endif; ?>
228
+
229
+
230
+ </table>
231
+
232
+ </div>
233
+
234
+
235
+ <div id="wplnst-content" class="wplnst-tab">
236
+
237
+ <table class="form-table">
238
+
239
+
240
+ <?php if (!empty($post_types)) : ?>
241
+
242
+ <?php if ($editable) : ?>
243
+
244
+ <tr>
245
+ <th<?php if ($post_types_error) echo ' style="color: red;"'; ?>><?php _e('Post types', 'wplnst'); ?></th>
246
+ <td class="wplnst-list"><?php foreach ($post_types as $key => $name) : ?><input <?php self::checked($key, $scan->post_types); ?> type="checkbox" name="ck-post-type[<?php echo $key; ?>]" id="ck-post-type-<?php echo $key; ?>" value="on" /><label for="ck-post-type-<?php echo $key; ?>"><?php echo $name; ?> (<code><?php echo $key; ?></code>)</label><br /><?php endforeach; ?></td>
247
+ </tr>
248
+
249
+ <?php else : ?>
250
+
251
+ <tr>
252
+ <th><?php _e('Post types', 'wplnst'); ?></th>
253
+ <td class="wplnst-value-list"><?php if (empty($scan->post_types_names_strict) || !is_array($scan->post_types_names_strict)) : echo '-'; else : echo implode("<br />", $scan->post_types_names_strict); endif; ?></td>
254
+ </tr>
255
+
256
+ <?php endif; ?>
257
+
258
+ <?php endif; ?>
259
+
260
+
261
+ <?php if (!empty($post_status)) : ?>
262
+
263
+ <?php if ($editable) : ?>
264
+
265
+ <tr>
266
+ <th<?php if ($post_status_error) echo ' style="color: red;"'; ?>><?php _e('Post status', 'wplnst'); ?></th>
267
+ <td class="wplnst-list"><?php foreach ($post_status as $key) : ?><input <?php self::checked($key, $scan->post_status); ?> type="checkbox" name="ck-post-status[<?php echo $key; ?>]" id="ck-post-status-<?php echo $key; ?>" value="on" /><label for="ck-post-status-<?php echo $key; ?>"><?php echo ucfirst($key); ?></label> &nbsp; <?php endforeach; ?></td>
268
+ </tr>
269
+
270
+ <?php else : ?>
271
+
272
+ <tr>
273
+ <th><?php _e('Post status', 'wplnst'); ?></th>
274
+ <td class="wplnst-value-list"><?php if (empty($scan->post_status_names) || !is_array($scan->post_status_names)) : echo '-'; else : echo implode(', ', array_map('esc_html', $scan->post_status_names)); endif; ?></td>
275
+ </tr>
276
+
277
+ <?php endif; ?>
278
+
279
+ <?php endif; ?>
280
+
281
+ <?php if ($editable) : ?>
282
+
283
+ <tr>
284
+ <th><label for="wplnst-cf-new"><?php _e('Custom fields', 'wplnst'); ?></label></th>
285
+ <td><table id="wplnst-elist-custom-fields" class="wplnst-elist" data-editable="true"></table>
286
+ <input type="text" id="wplnst-cf-new" value="" class="regular-text" placeholder="<?php _e('Custom field key', 'wplnst'); ?>" />&nbsp;<select id="wplnst-cf-new-type"><?php self::options($custom_fields, false); ?></select>&nbsp;<input class="button-secondary" type="button" id="wplnst-cf-new-add" value="<?php _e('Add', 'wplnst'); ?>" /></td>
287
+ </tr>
288
+
289
+ <?php else : ?>
290
+
291
+ <tr>
292
+ <th><?php _e('Custom fields', 'wplnst'); ?></th>
293
+ <td><?php if (empty($scan->custom_fields)) : ?>-<?php else : ?><table id="wplnst-elist-custom-fields" class="wplnst-elist wplnst-elist-readonly" data-editable="false"></table><?php endif; ?></td>
294
+ </tr>
295
+
296
+ <?php endif; ?>
297
+
298
+ <?php if ($editable) : ?>
299
+
300
+ <tr>
301
+ <th<?php if ($post_types_error) echo ' style="color: red;"'; ?>><?php _e('Comment links', 'wplnst'); ?></th>
302
+ <td class="wplnst-list"><?php foreach ($comment_types as $key => $name) : ?><input <?php self::checked($key, $scan->comment_types); ?> type="checkbox" name="ck-comment-type[<?php echo $key; ?>]" id="ck-comment-type-<?php echo $key; ?>" value="on" /><label for="ck-comment-type-<?php echo $key; ?>"><?php echo $name; ?></label> &nbsp; <?php endforeach; ?></td>
303
+ </tr>
304
+
305
+ <?php else : ?>
306
+
307
+ <tr>
308
+ <th><?php _e('Comment links', 'wplnst'); ?></th>
309
+ <td class="wplnst-value-list"><?php if (!empty($scan->comment_types_names) && is_array($scan->comment_types_names)) echo implode(', ', array_map('esc_html', $scan->comment_types_names)); ?></td>
310
+ </tr>
311
+
312
+ <?php endif; ?>
313
+
314
+ <?php if ($editable) : ?>
315
+
316
+ <tr>
317
+ <th<?php if ($post_types_error) echo ' style="color: red;"'; ?>><?php _e('Also check links in', 'wplnst'); ?></th>
318
+ <td class="wplnst-list"><input <?php self::checked($scan->check_blogroll, true); ?> type="checkbox" name="ck-blogroll" id="ck-blogroll" value="on" /><label for="ck-blogroll"><?php _e('Blogroll links', 'wplnst'); ?></label></td>
319
+ </tr>
320
+
321
+ <?php else : ?>
322
+
323
+ <tr>
324
+ <th><?php _e('Also check links in', 'wplnst'); ?></th>
325
+ <td class="wplnst-value-list"><?php echo $scan->check_blogroll? __('Blogroll links', 'wplnst') : '-'; ?></td>
326
+ </tr>
327
+
328
+ <?php endif; ?>
329
+ </table>
330
+
331
+ </div>
332
+
333
+
334
+ <div id="wplnst-filters" class="wplnst-tab">
335
+
336
+ <table class="form-table">
337
+
338
+ <?php if ($editable) : ?>
339
+
340
+ <tr>
341
+ <th style="width: 120px;"><?php _e('Anchor filters', 'wplnst'); ?></th>
342
+ <td><table id="wplnst-elist-anchor-filters" class="wplnst-elist" cellspacing="0" cellpadding="0" border="0" data-editable="true" data-label="<?php _e('Anchor text', 'wplnst'); ?>"></table>
343
+ <?php _e('Anchor text', 'wplnst'); ?> <select id="wplnst-af-new-type"><?php self::options($anchor_filters, false); ?></select>&nbsp;
344
+ <input id="wplnst-af-new" type="text" class="regular-text" value="" placeholder="<?php _e('Anchor text filter', 'wplnst'); ?>" />&nbsp;
345
+ <input class="button-secondary" type="button" id="wplnst-af-new-add" value="<?php _e('Add', 'wplnst'); ?>" /></td>
346
+ </tr>
347
+
348
+ <?php else : ?>
349
+
350
+ <tr>
351
+ <th style="width: 120px;"><?php _e('Anchor filters', 'wplnst'); ?></th>
352
+ <td><?php if (empty($scan->anchor_filters)) : ?>-<?php else : ?><table id="wplnst-elist-anchor-filters" class="wplnst-elist wplnst-elist-readonly" cellspacing="0" cellpadding="0" border="0" data-label="<?php _e('Anchor text', 'wplnst'); ?>" data-editable="false"></table><?php endif; ?></td>
353
+ </tr>
354
+
355
+ <?php endif; ?>
356
+
357
+ </table>
358
+
359
+ <table class="form-table">
360
+
361
+ <?php if ($editable) : ?>
362
+
363
+ <tr>
364
+ <th style="width: 120px;"><?php _e('Include URLs', 'wplnst'); ?></th>
365
+ <td><table id="wplnst-elist-include-urls" class="wplnst-elist" cellspacing="0" cellpadding="0" border="0" data-editable="true"></table>
366
+ <input id="wplnst-ius-new" type="text" class="regular-text" value="" />&nbsp;
367
+ <select id="wplnst-ius-new-type"><?php self::options($url_filters, false); ?></select>&nbsp;
368
+ <input class="button-secondary" type="button" id="wplnst-ius-new-add" value="<?php _e('Add', 'wplnst'); ?>" /></td>
369
+ </tr>
370
+
371
+ <?php else : ?>
372
+
373
+ <tr>
374
+ <th style="width: 120px;"><?php _e('Include URLs', 'wplnst'); ?></th>
375
+ <td><?php if (empty($scan->include_urls)) : ?>-<?php else : ?><table id="wplnst-elist-include-urls" class="wplnst-elist wplnst-elist-readonly" cellspacing="0" cellpadding="0" border="0" data-editable="false"></table><?php endif; ?></td>
376
+ </tr>
377
+
378
+ <?php endif; ?>
379
+
380
+ <?php if ($editable) : ?>
381
+
382
+ <tr>
383
+ <th style="width: 120px;"><?php _e('Exclude URLs', 'wplnst'); ?></th>
384
+ <td><table id="wplnst-elist-exclude-urls" class="wplnst-elist" cellspacing="0" cellpadding="0" border="0" data-editable="true"></table>
385
+ <input id="wplnst-eus-new" type="text" class="regular-text" value="" />&nbsp;
386
+ <select id="wplnst-eus-new-type"><?php self::options($url_filters, false); ?></select>&nbsp;
387
+ <input class="button-secondary" type="button" id="wplnst-eus-new-add" value="<?php _e('Add', 'wplnst'); ?>" /></td>
388
+ </tr>
389
+
390
+ <?php else : ?>
391
+
392
+ <tr>
393
+ <th style="width: 120px;"><?php _e('Exclude URLs', 'wplnst'); ?></th>
394
+ <td><?php if (empty($scan->exclude_urls)) : ?>-<?php else : ?><table id="wplnst-elist-exclude-urls" class="wplnst-elist wplnst-elist-readonly" cellspacing="0" cellpadding="0" border="0" data-editable="false"></table><?php endif; ?></td>
395
+ </tr>
396
+
397
+ <?php endif; ?>
398
+
399
+ </table>
400
+
401
+ <table class="form-table">
402
+
403
+ <?php if ($editable) : ?>
404
+
405
+ <tr>
406
+ <th style="width: 120px;"><?php _e('HTML attributes', 'wplnst'); ?></th>
407
+ <td><table id="wplnst-elist-html-attributes" class="wplnst-elist" cellspacing="0" cellpadding="0" border="0" data-editable="true"></table>
408
+ <select id="wplnst-hes-new" style="width: 55px;"><option value="a">a</option><option value="img">img</option></select>&nbsp;
409
+ <select id="wplnst-hes-new-have"><?php self::options($html_attributes_having, false); ?></select>&nbsp;
410
+ <input id="wplnst-hes-new-att" type="text" class="regular-text" value="" placeholder="<?php _e('Attribute name', 'wplnst'); ?>" style="width: 135px;" />&nbsp;
411
+ <select id="wplnst-hes-new-op"><?php self::options($html_attributes_operators, false); ?></select>&nbsp;
412
+ <input id="wplnst-hes-new-val" type="text" class="regular-text" value="" placeholder="<?php _e('Attribute value', 'wplnst'); ?>" style="width: 135px;" />&nbsp;
413
+ <input id="wplnst-hes-new-add" class="button-secondary" type="button" value="<?php _e('Add', 'wplnst'); ?>" /></td>
414
+ </tr>
415
+
416
+ <?php else : ?>
417
+
418
+ <tr>
419
+ <th style="width: 120px;"><?php _e('HTML attributes', 'wplnst'); ?></th>
420
+ <td><?php if (empty($scan->html_attributes)) : ?>-<?php else : ?><table id="wplnst-elist-html-attributes" class="wplnst-elist wplnst-elist-readonly" cellspacing="0" cellpadding="0" border="0" data-editable="false"></table><?php endif; ?></td>
421
+ </tr>
422
+
423
+ <?php endif; ?>
424
+
425
+ </table>
426
+
427
+ <table class="form-table">
428
+
429
+ <?php $label = __('Accelerate crawling process integrating filters in main database query', 'wplnst'); ?>
430
+
431
+ <?php if ($editable) : ?>
432
+
433
+ <tr><td><input <?php self::checked($scan->filtered_query, true); ?> type="checkbox" id="wplnst-filtered-query" name="ck-filtered-query" value="on" /><label for="wplnst-filtered-query">&nbsp;<?php echo $label; ?></label></td></tr>
434
+
435
+ <?php elseif (!empty($scan->anchor_filters) || !empty($scan->include_urls) || !empty($scan->exclude_urls) || !empty($scan->html_attributes)) : ?>
436
+
437
+ <tr><td><?php echo $label; ?>: <strong><?php echo $scan->filtered_query? __('Yes', 'wplnst') : __('No', 'wplnst'); ?></strong></td></tr>
438
+
439
+ <?php endif; ?>
440
+
441
+ </table>
442
+
443
+ </div>
444
+
445
+
446
+ <div id="wplnst-status" class="wplnst-tab">
447
+
448
+ <table class="form-table">
449
+
450
+ <?php if ($editable) : ?>
451
+
452
+ <tr>
453
+ <th<?php if ($link_status_error) echo ' style="color: red;"'; ?>><?php _e('Track links by level', 'wplnst'); ?></th>
454
+ <td class="wplnst-list"><?php foreach ($status_levels as $key => $value) : ?><input <?php self::checked($key, $scan->status_levels); ?> type="checkbox" name="ck-status-level[<?php echo $key; ?>]" id="ck-status-level-<?php echo $key; ?>" class="wplnst-status-level" value="on" /><label for="ck-status-level-<?php echo $key; ?>"><strong><?php echo $key; ?>00s</strong> <?php echo $value; ?></label><br /><?php endforeach; ?></td>
455
+ </tr>
456
+
457
+ <?php else : ?>
458
+
459
+ <tr>
460
+ <th><?php _e('Track links by level', 'wplnst'); ?></th>
461
+ <td class="wplnst-value-list"><?php if (empty($scan->status_levels_names) || !is_array($scan->status_levels_names)) : echo '-'; else : echo implode("<br />", array_map('esc_html', $scan->status_levels_names)); endif; ?></td>
462
+ </tr>
463
+
464
+ <?php endif; ?>
465
+
466
+ <?php if ($editable) : ?>
467
+
468
+ <tr>
469
+ <th<?php if ($link_status_error) echo ' style="color: red;"'; ?>><?php _e('Track links by code', 'wplnst'); ?></th>
470
+ <td class="wplnst-list"><table cellpadding="0" cellspacing="0" style="margin: 2px 0 0; padding: 0;">
471
+ <?php foreach ($status_codes as $level => $codes) : ?>
472
+ <?php $codes_keys = array_keys($codes); $num = 0; $inc = count($codes) / 2; $inc = ($inc != floor($inc))? floor($inc) : $inc - 1; foreach ($codes as $code => $name) : $num++; $inc++; ?>
473
+ <tr style="margin: 0; padding: 0 0 25px;">
474
+ <td style="margin: 0; padding: 0 25px 5px 0;"><input <?php self::checked($code, $scan->status_codes); ?> type="checkbox" name="ck-status-code[<?php echo $code; ?>]" id="ck-status-code-<?php echo $code; ?>" value="on" class="wplnst-code-level wplnst-code-level-<?php echo $level; ?>" /><label for="ck-status-code-<?php echo $code; ?>"><strong><?php echo $code; ?></strong> <?php echo $name; ?></label></td>
475
+ <td style="margin: 0; padding: 0;"><?php if ($inc < count($codes)) : $code_r = $codes_keys[$inc]; ?><input <?php self::checked($code_r, $scan->status_codes); ?> type="checkbox" name="ck-status-code[<?php echo $code_r; ?>]" id="ck-status-code-<?php echo $code_r; ?>" value="on" class="wplnst-code-level wplnst-code-level-<?php echo $level; ?>" /><label for="ck-status-code-<?php echo $code_r; ?>"><strong><?php echo $code_r; ?></strong> <?php echo $codes[$code_r]; ?></label><?php endif; ?></td>
476
+ </tr>
477
+ <?php if ($num >= count($codes) / 2) break; ?>
478
+ <?php endforeach; ?>
479
+ <tr style="margin: 0; padding: 0;">
480
+ <td colspan="2" style="margin: 0; padding: 0;">&nbsp;</td>
481
+ </tr>
482
+ <?php endforeach; ?>
483
+ </table></td>
484
+ </tr>
485
+
486
+ <?php else : ?>
487
+
488
+ <tr>
489
+ <th><?php _e('Track links by code', 'wplnst'); ?></th>
490
+ <td class="wplnst-value-list"><?php if (empty($scan->status_codes_names) || !is_array($scan->status_codes_names)) : echo '-'; else : echo implode("<br />", array_map('esc_html', $scan->status_codes_names)); endif; ?></td>
491
+ </tr>
492
+
493
+ <?php endif; ?>
494
+
495
+ </table>
496
+
497
+ </div>
498
+
499
+
500
+ <?php if (false) : ?><div id="wplnst-scheduled" class="wplnst-tab">
501
+
502
+ </div><?php endif; ?>
503
+
504
+
505
+ <div id="wplnst-advanced" class="wplnst-tab">
506
+
507
+ <?php if ('end' == $scan->status) : ?>
508
+
509
+ <table class="form-table">
510
+ <tr>
511
+ <th><?php _e('Number of threads', 'wplnst'); ?></th>
512
+ <td class="wplnst-value"><?php echo empty($scan->threads->max)? '-' : esc_html($scan->threads->max); ?></td>
513
+ </tr>
514
+ <tr>
515
+ <th><?php _e('Connection timeout', 'wplnst'); ?></th>
516
+ <td class="wplnst-value"><?php echo empty($scan->threads->connect_timeout)? '-' : esc_html($scan->threads->connect_timeout).' '.__('seconds', 'wplnst'); ?></td>
517
+ </tr>
518
+ <tr>
519
+ <th><?php _e('Request timeout', 'wplnst'); ?></th>
520
+ <td class="wplnst-value"><?php echo empty($scan->threads->request_timeout)? '-' : esc_html($scan->threads->request_timeout).' '.__('seconds', 'wplnst'); ?></td>
521
+ </tr>
522
+ </table>
523
+
524
+ <?php else : ?>
525
+
526
+ <p><?php _e('All these values are optional, leave them empty to use plugin defaults.', 'wplnst'); ?></p>
527
+
528
+ <table class="form-table">
529
+ <tr>
530
+ <th><label for="tx-threads"><?php _e('Number of threads', 'wplnst'); ?></label></th>
531
+ <td><input type="text" name="tx-threads" id="tx-threads" value="<?php echo empty($scan->threads->max)? '' : esc_attr($scan->threads->max); ?>" class="small-text" /> <?php printf(__('(optional, default %d threads)', 'wplnst'), $default_max_threads); ?></td>
532
+ </tr>
533
+ <tr>
534
+ <th><label for="tx-connect-timeout"><?php _e('Connection timeout', 'wplnst'); ?></label></th>
535
+ <td><input type="text" name="tx-connect-timeout" id="tx-connect-timeout" value="<?php echo empty($scan->threads->connect_timeout)? '' : esc_attr($scan->threads->connect_timeout); ?>" class="small-text" /> <?php _e('seconds', 'wplnst'); ?> <?php printf(__('(optional, default %d seconds)', 'wplnst'), $default_connect_timeout); ?></td>
536
+ </tr>
537
+ <tr>
538
+ <th><label for="tx-request-timeout"><?php _e('Request timeout', 'wplnst'); ?></label></th>
539
+ <td><input type="text" name="tx-request-timeout" id="tx-request-timeout" value="<?php echo empty($scan->threads->request_timeout)? '' : esc_attr($scan->threads->request_timeout); ?>" class="small-text" /> <?php _e('seconds', 'wplnst'); ?> <?php printf(__('(optional, default %d seconds)', 'wplnst'), $default_request_timeout); ?></td>
540
+ </tr>
541
+ </table>
542
+
543
+ <?php endif; ?>
544
+
545
+ </div>
546
+
547
+
548
+ <p><input type="submit" value="<?php _e('Save scan changes', 'wplnst'); ?>" class="button button-primary" />
549
+ <?php if ($is_ready && ('wait' == $scan->status)) : ?> &nbsp; <input id="wplnst-save-and-run" type="button" value="<?php _e('Save and run crawler', 'wplnst'); ?>" class="button" /><?php endif; ?>
550
+ <?php if ($scan->id > 0) : ?> &nbsp;&nbsp; <a href="<?php echo esc_url(WPLNST_Core_Plugin::get_url_scans_delete($scan->id, $scan->hash)); ?>" class="wplnst-scan-delete-isolated wplnst-trash-editor" data-confirm-delete="<?php echo esc_attr(WPLNST_Admin::get_text('scan_delete_confirm')); ?>"><?php _e('Delete this scan', 'wplnst'); ?></a><?php endif; ?></p>
551
+
552
+ </div>
553
+
554
+ </form><?php
555
+ }
556
+
557
+
558
+
559
+ }
views/scans-results.php ADDED
@@ -0,0 +1,1186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Check WP constant
4
+ if (!defined('ABSPATH'))
5
+ die;
6
+
7
+ // Check dependencies
8
+ if(!class_exists('WP_List_Table'))
9
+ require_once(ABSPATH.'wp-admin/includes/class-wp-list-table.php');
10
+
11
+ /**
12
+ * WP Link Status Views Scans Results class
13
+ *
14
+ * @package WP Link Status
15
+ * @subpackage WP Link Status Views
16
+ */
17
+ class WPLNST_Views_Scans_Results extends WP_List_Table {
18
+
19
+
20
+
21
+ // Properties
22
+ // ---------------------------------------------------------------------------------------------------
23
+
24
+
25
+
26
+ /**
27
+ * External data
28
+ */
29
+ protected $results;
30
+
31
+
32
+
33
+ /**
34
+ * Base URL for filters
35
+ */
36
+ protected $base_url;
37
+
38
+
39
+
40
+ // Initialization
41
+ // ---------------------------------------------------------------------------------------------------
42
+
43
+
44
+
45
+ /**
46
+ * Constructor
47
+ */
48
+ public function __construct($results) {
49
+
50
+ // Dependencies
51
+ wplnst_require('core', 'util-math');
52
+ wplnst_require('core', 'util-string');
53
+
54
+ // Parent constructor
55
+ parent::__construct();
56
+
57
+ // Copy results
58
+ $this->results = $results;
59
+
60
+ // Base link for filters
61
+ $this->base_url = esc_url(WPLNST_Core_Plugin::get_url_scans_results($this->results->scan->id));
62
+ }
63
+
64
+
65
+
66
+ // Prepare columns and items
67
+ // ---------------------------------------------------------------------------------------------------
68
+
69
+
70
+
71
+ /**
72
+ * Prepare columns and data
73
+ */
74
+ function prepare_items() {
75
+
76
+ // Columns
77
+ $this->setup_columns();
78
+
79
+ // Data items
80
+ $this->setup_items();
81
+
82
+ // Pagination
83
+ $this->setup_pagination();
84
+ }
85
+
86
+
87
+
88
+ /**
89
+ * Setup columns
90
+ */
91
+ private function setup_columns() {
92
+
93
+ // Initialize
94
+ $hidden = array();
95
+ $sortable = array();
96
+
97
+ // Column headers
98
+ $this->_column_headers = array($this->get_columns(), $hidden, $sortable);
99
+ }
100
+
101
+
102
+
103
+ /**
104
+ * Columns array
105
+ */
106
+ public function get_columns(){
107
+
108
+ // Prepare
109
+ $columns = array(
110
+ 'cb' => '<input type="checkbox" />',
111
+ 'wplnst-url' => 'URL',
112
+ 'wplnst-status' => __('Status', 'wplnst'),
113
+ 'wplnst-anchor' => __('Anchor text', 'wplnst'),
114
+ 'wplnst-content' => __('Content', 'wplnst'),
115
+ );
116
+
117
+ // Exception
118
+ if ($this->results->isolated) {
119
+ unset($columns['cb']);
120
+ unset($columns['wplnst-content']);
121
+ } elseif (!$this->get_columns_cb()) {
122
+ unset($columns['cb']);
123
+ }
124
+
125
+ // Done
126
+ return $columns;
127
+ }
128
+
129
+
130
+
131
+ /**
132
+ * Check if needed a cb column
133
+ */
134
+ protected function get_columns_cb() {
135
+ return false;
136
+ }
137
+
138
+
139
+
140
+ /**
141
+ * Setup data items
142
+ */
143
+ private function setup_items() {
144
+
145
+ // Dependencies
146
+ wplnst_require('core', 'types-curl');
147
+
148
+ // Initialize
149
+ $status_levels = WPLNST_Core_Types::get_status_levels();
150
+ $status_codes = WPLNST_Core_Types::get_status_codes_raw();
151
+
152
+ // Populate data
153
+ $this->items = array();
154
+ foreach ($this->results->rows as $row) {
155
+
156
+
157
+ // Normalize identifier
158
+ $item = array(
159
+ 'ID' => $row->url_id,
160
+ 'url' => $row->url,
161
+ 'url_id' => $row->url_id,
162
+ 'loc_id' => $row->loc_id,
163
+ 'status_level' => $row->status_level,
164
+ 'redirect_url' => $row->redirect_url,
165
+ 'redirect_url_id' => $row->redirect_url_id,
166
+ 'object_id' => $row->object_id,
167
+ 'object_type' => $row->object_type,
168
+ 'object_field' => $row->object_field,
169
+ 'link_type' => $row->link_type,
170
+ 'ignored' => $row->ignored,
171
+ 'unlinked' => $row->unlinked,
172
+ 'nofollow' => $row->nofollow,
173
+ 'anchored' => $row->anchored,
174
+ 'attributed' => $row->attributed,
175
+ );
176
+
177
+
178
+ /* First column: URL */
179
+
180
+
181
+ // Add unlinked flag
182
+ $url = '<span id="wplnst-results-url-unlinked-'.$row->loc_id.'" class="wplnst-results-mark wplnst-results-mark-unlinked'.($row->unlinked? '' : ' wplnst-display-none').'">'.__('Unlinked', 'wplnst').'</span>';
183
+
184
+ // Check link
185
+ if ('http' == $row->scheme || 'https' == $row->scheme || 'ftp' == $row->scheme) {
186
+
187
+ // Link to an browser resource
188
+ $url .= '<strong><a href="'.esc_url($row->url).'" target="_blank" id="wplnst-results-url-loc-'.$row->loc_id.'" title="'.esc_url($row->url).'">'.esc_html($row->raw_url).'</a></strong>';
189
+
190
+ // No link
191
+ } else {
192
+
193
+ // Unsupported linkable protocol
194
+ $url .= '<strong><span id="wplnst-results-url-loc-'.$row->loc_id.'">'.esc_html($row->raw_url).'</span></strong>';
195
+ }
196
+
197
+
198
+ // Redirection
199
+ $class_redirection = ('3' == $row->status_level && $row->redirect_url_id > 0 && !empty($row->redirect_url))? '' : 'wplnst-display-none';
200
+ $url .= '<span id="wplnst-results-url-redir-'.$row->loc_id.'" class="'.$class_redirection.'"><br />&rarr;&nbsp;<a id="wplnst-results-url-redir-href-'.$row->loc_id.'" href="'.esc_url($row->redirect_url).'" target="_blank">'.esc_html($row->redirect_url).'</a></span>';
201
+
202
+
203
+ // Check error
204
+ $class_error = (!empty($row->status_code) || empty($row->curl_errno))? 'wplnst-display-none' : '';
205
+ $url .= '<div id="wplnst-results-url-error-'.$row->loc_id.'" class="'.$class_error.'">';
206
+ if (empty($row->status_code) && !empty($row->curl_errno)) {
207
+
208
+ // Retrieve error type
209
+ $curl_error = WPLNST_Core_Types_CURL::get_code_info($row->curl_errno);
210
+
211
+ // Unknown
212
+ if (empty($curl_error)) {
213
+ $url .= '<strong id="wplnst-results-url-error-title-'.$row->loc_id.'">'.__('Error code ', 'wplnst').$row->curl_errno.'</strong> <span id="wplnst-results-url-error-code-'.$row->loc_id.'" class="wplnst-results-url-error-code"></span><br /><span id="wplnst-results-url-error-desc-'.$row->loc_id.'"></span>';
214
+
215
+ // Knowed error
216
+ } else {
217
+ $url .= '<strong id="wplnst-results-url-error-title-'.$row->loc_id.'">'.esc_html($curl_error['title']).'</strong> <span id="wplnst-results-url-error-code-'.$row->loc_id.'" class="wplnst-results-url-error-code">'.esc_html(sprintf(__('error code %d', 'wplnst'), $row->curl_errno)).'</span><br /><span id="wplnst-results-url-error-desc-'.$row->loc_id.'">'.esc_html($curl_error['desc']).'</span>';
218
+ }
219
+ } else {
220
+ $url .= '<strong id="wplnst-results-url-error-title-'.$row->loc_id.'"></strong> <span id="wplnst-results-url-error-code-'.$row->loc_id.'" class="wplnst-results-url-error-code"></span><br /><span id="wplnst-results-url-error-desc-'.$row->loc_id.'"></span>';
221
+ }
222
+ $url .= '</div>';
223
+
224
+
225
+ // Check redirection error
226
+ $error_redir = !empty($row->redirect_url_id) && empty($row->redirect_url_status) && !empty($row->redirect_curl_errno);
227
+ $class_error_redir = $error_redir? '' : 'wplnst-display-none';
228
+ $url .= '<div id="wplnst-results-url-error-redir-'.$row->loc_id.'" class="'.$class_error_redir.'">&rarr;&nbsp;';
229
+ if ($error_redir) {
230
+
231
+ // Retrieve error type
232
+ $curl_error = WPLNST_Core_Types_CURL::get_code_info($row->redirect_curl_errno);
233
+
234
+ // Unknown
235
+ if (empty($curl_error)) {
236
+ $url .= '<strong id="wplnst-results-url-error-redir-title-'.$row->loc_id.'">'.__('Error code ', 'wplnst').$row->curl_errno.'</strong> <span id="wplnst-results-url-error-redir-code-'.$row->loc_id.'" class="wplnst-results-url-error-code"></span><br /><span id="wplnst-results-url-error-redir-desc-'.$row->loc_id.'"></span>';
237
+
238
+ // Knowed error
239
+ } else {
240
+ $url .= '<strong id="wplnst-results-url-error-redir-title-'.$row->loc_id.'">'.esc_html($curl_error['title']).'</strong> <span id="wplnst-results-url-error-redir-code-'.$row->loc_id.'" class="wplnst-results-url-error-code">'.esc_html(sprintf(__('error code %d', 'wplnst'), $row->redirect_curl_errno)).'</span><br /><span id="wplnst-results-url-error-redir-desc-'.$row->loc_id.'">'.esc_html($curl_error['desc']).'</span>';
241
+ }
242
+ } else {
243
+ $url .= '<strong id="wplnst-results-url-error-redir-title-'.$row->loc_id.'"></strong> <span id="wplnst-results-url-error-redir-code-'.$row->loc_id.'" class="wplnst-results-url-error-code"></span><br /><span id="wplnst-results-url-error-redir-desc-'.$row->loc_id.'"></span>';
244
+ }
245
+ $url .= '</div>';
246
+
247
+ // Check relative or absolute
248
+ $url .= '<div id="wplnst-results-url-full-'.$row->loc_id.'" class="wplnst-results-url-full'.(($row->relative || $row->absolute)? '' : ' wplnst-display-none').'">'.esc_html($row->url).'</div>';
249
+
250
+
251
+ // Prepare data for https mark
252
+ $is_https = ('https' == $row->scheme);
253
+
254
+ // Prepare data for redirections
255
+ $redirs_count = 0;
256
+ if ($redirs = ('3' == $row->status_level && $row->redirect_url_id > 0 && !empty($row->redirect_url))) {
257
+ $redirs_steps = @json_decode($row->redirect_steps, true);
258
+ if (!empty($redirs_steps) && is_array($redirs_steps))
259
+ $redirs_count = count($redirs_steps);
260
+ }
261
+
262
+ // Prepare row of marks
263
+ $mark_modified = '<span class="wplnst-results-mark wplnst-results-mark-modified' .($row->modified? '' : ' wplnst-display-none').'">'.__('Modified', 'wplnst').'</span>';
264
+ $mark_nofollow = '<span class="wplnst-results-mark wplnst-results-mark-nofollow' .($row->nofollow? '' : ' wplnst-display-none').'">nofollow</span>';
265
+ $mark_relative = '<span class="wplnst-results-mark wplnst-results-mark-relative' .($row->relative? '' : ' wplnst-display-none').'">'.__('Relative', 'wplnst').'</span>';
266
+ $mark_absolute = '<span class="wplnst-results-mark wplnst-results-mark-absolute' .($row->absolute? '' : ' wplnst-display-none').'">'.__('Absolute', 'wplnst').'</span>';
267
+ $mark_spaced = '<span class="wplnst-results-mark wplnst-results-mark-spaced' .($row->spaced? '' : ' wplnst-display-none').'">'.__('Spaced', 'wplnst').'</span>';
268
+ $mark_malformed = '<span class="wplnst-results-mark wplnst-results-mark-malformed'.($row->malformed? '' : ' wplnst-display-none').'">'.__('Malformed', 'wplnst').'</span>';
269
+ $mark_https = '<span class="wplnst-results-mark wplnst-results-mark-https' .($is_https? '' : ' wplnst-display-none').'">HTTPS</span>';
270
+ $mark_protorel = '<span class="wplnst-results-mark wplnst-results-mark-protorel' .($row->protorel? '' : ' wplnst-display-none').'">'.__('Protocol relative', 'wplnst').'</span>';
271
+ $mark_ignored = '<span class="wplnst-results-mark wplnst-results-mark-ignored' .($row->ignored? '' : ' wplnst-display-none').'">'.__('Ignored', 'wplnst').'</span>';
272
+ $mark_redirs = '<span class="wplnst-results-mark wplnst-results-mark-redirs' .($redirs? '' : ' wplnst-display-none').'">'.((!$redirs || empty($redirs_count) || 1 == $redirs_count)? '1 redirect' : $redirs_count.' redirects').'</span>';
273
+
274
+ // Add new row checking visibility
275
+ $mark_visible = ($row->modified || $row->nofollow || $row->relative || $row->absolute || $row->spaced || $row->malformed || $is_https || $row->protorel || $row->ignored || $redirs);
276
+ $url .= '<div id="wplnst-results-url-marks-'.$row->loc_id.'" class="wplnst-results-url-marks'.($mark_visible? '' : ' wplnst-display-none').'">'.$mark_modified.$mark_nofollow.$mark_relative.$mark_absolute.$mark_spaced.$mark_malformed.$mark_https.$mark_protorel.$mark_ignored.$mark_redirs.'</div>';
277
+
278
+
279
+ // Done
280
+ $item['wplnst-url'] = '<div class="wplnst-row-url">'.$url.'</div>';
281
+
282
+
283
+
284
+ /* Second column: status, redirection status, and time/size info */
285
+
286
+ // Start container
287
+ $item['wplnst-status'] = '<div class="wplnst-url-status-code">';
288
+
289
+ // Prepare status classes
290
+ $class_status_error = empty($row->curl_errno)? ' wplnst-display-none' : '';
291
+ $class_status_code = empty($row->status_code)? ' wplnst-display-none' : '';
292
+
293
+ // Prepare status Code
294
+ $status_code_label = isset($status_codes[$row->status_code])? ' '.$status_codes[$row->status_code] : '';
295
+
296
+ // Prepare rechecked mark
297
+ $mark_rechecked = ' &nbsp; <span id="wplnst-url-status-recheck-mark-'.$row->loc_id.'" class="wplnst-results-mark wplnst-results-mark-rechecked'.($row->rechecked? '' : ' wplnst-display-none').'">Rechecked</span>';
298
+
299
+ // Status code result
300
+ $item['wplnst-status'] .= '<div class="wplnst-url-status-code-result"><span id="wplnst-url-status-code-0-loc-'.$row->loc_id.'" class="wplnst-url-status-code-0'.$class_status_error.'">'.__('Request error', 'wplnst').'</span><span id="wplnst-url-status-code-loc-'.$row->loc_id.'" class="wplnst-url-status-code-'.esc_attr($row->status_level).$class_status_code.'">'.esc_html($row->status_code.$status_code_label).'</span>'.$mark_rechecked.'</div>';
301
+
302
+ // Prepare redirections status
303
+ $redir_status_level = $redir_status_label = '';
304
+ $status_redir = (!empty($row->redirect_url_id) && (!empty($row->redirect_url_status) || !empty($row->redirect_curl_errno)));
305
+ if ($status_redir && !empty($row->redirect_url_status)) {
306
+ $redir_status_level = mb_substr($row->redirect_url_status, 0, 1);
307
+ $redir_status_label = $row->redirect_url_status.(isset($status_codes[$row->redirect_url_status])? ' '.$status_codes[$row->redirect_url_status] : '');
308
+ }
309
+
310
+ // Prepare redirection classes
311
+ $class_status_redir = $status_redir? '' : ' wplnst-display-none';
312
+ $class_status_redir_error = ($status_redir && !empty($row->redirect_curl_errno))? '' : ' wplnst-display-none';
313
+ $class_status_redir_code = ($status_redir && !empty($row->redirect_url_status))? '' : ' wplnst-display-none';
314
+
315
+ // Redirection status code
316
+ $item['wplnst-status'] .= '<div id="wplnst-url-status-code-redir-'.$row->loc_id.'" class="wplnst-url-status-code-redir'.$class_status_redir.'"><span class="wplnst-url-status-code-redir-arrow">&rarr;&nbsp;</span><span id="wplnst-url-status-code-redir-error-'.$row->loc_id.'" class="wplnst-url-status-code-0'.$class_status_redir_error.'">'.__('Request error', 'wplnst').'</span><span id="wplnst-url-status-code-redir-status-'.$row->loc_id.'" class="wplnst-url-status-code-'.esc_attr($redir_status_level).$class_status_redir_code.'">'.esc_html($redir_status_label).'</span></div>';
317
+
318
+ // End status container
319
+ $item['wplnst-status'] .= '</div>';
320
+
321
+ // Time and size
322
+ $item['wplnst-status'] .= '<div class="wplnst-url-status-info"><span id="wplnst-url-status-info-time-'.$row->loc_id.'">'.number_format_i18n($row->total_time, 3).' s</span>'.(($row->total_bytes > 0)? '<span id="wplnst-url-status-info-split-'.$row->loc_id.'" class="wplnst-url-status-info-split"> | </span><span id="wplnst-url-status-info-size-'.$row->loc_id.'">'.wplnst_format_bytes($row->total_bytes).'</span>' : '<span id="wplnst-url-status-info-split-'.$row->loc_id.'" class="wplnst-url-status-info-split wplnst-display-none"> | </span><span id="wplnst-url-status-info-size-'.$row->loc_id.'" class="wplnst-display-none"></span>').'</div>';
323
+
324
+
325
+
326
+ /* Third column: anchor text */
327
+
328
+ // Set text anchor
329
+ if ('links' == $row->link_type) {
330
+
331
+ // Prepare modified mark
332
+ $mark_anchored = '<div id="wplnst-results-anchor-mod-'.$row->loc_id.'" class="wplnst-results-anchor-mod'.($row->anchored? '' : ' wplnst-display-none').'"><span class="wplnst-results-mark wplnst-results-mark-modified">'.__('Modified', 'wplnst').'</span></div>';
333
+
334
+ // Anchor text
335
+ $item['wplnst-anchor'] = '<div class="wplnst-anchor-link"><span id="wplnst-results-anchor-loc-'.$row->loc_id.'">'.esc_html($row->anchor).'</span>'.$mark_anchored.'</div>';
336
+
337
+ // Is an image
338
+ } elseif ('images' == $row->link_type) {
339
+
340
+ // Image info
341
+ $item['wplnst-anchor'] = '<div class="wplnst-anchor-image wplnst-row-dashicon"><span>'.esc_html(__('Image', 'wplnst')).'</span></div>';
342
+ }
343
+
344
+
345
+
346
+ /* Fourth column: Content host */
347
+
348
+ // Check no isolated
349
+ if (!$this->results->isolated) {
350
+
351
+ // Default content link
352
+ $item['wplnst-content'] = $row->object_type.' '.$row->object_id;
353
+
354
+ // Extract identifier
355
+ $object_id = (int) $row->object_id;
356
+
357
+ // Column content for posts
358
+ if ('posts' == $row->object_type) {
359
+
360
+ // Retrieve post
361
+ $post = get_post($object_id);
362
+
363
+ // Check post object
364
+ if (!empty($post) && is_object($post) && 'WP_Post' == get_class($post)) {
365
+
366
+ // Copy object
367
+ $item['post'] = $post;
368
+ $item['can_edit'] = current_user_can('edit_post', $post->ID);
369
+
370
+ // Prepare title
371
+ $title = _draft_or_post_title($post);
372
+
373
+ // Check edit post link
374
+ if ($item['can_edit'] && 'trash' != $post->post_status) {
375
+
376
+ // Copy edit post link
377
+ $item['edit_post_link'] = get_edit_post_link($post->ID);
378
+
379
+ // Editable post link
380
+ $post_row = '<strong><a href="'.$item['edit_post_link'].'" title="'.esc_attr(sprintf(__( 'Edit &#8220;%s&#8221;'), $title)).'" target="_blank">'.$title.'</a></strong>';
381
+
382
+ // Post without link
383
+ } else {
384
+
385
+ // Only title
386
+ $post_row = '<strong>'.$title.'</strong>';
387
+ }
388
+
389
+ // Not found
390
+ } else {
391
+
392
+ // Not found item
393
+ $post_row = sprintf(__('Entry %d not found', 'wplnst'), $object_id);
394
+ }
395
+
396
+ // Set column value
397
+ $item['wplnst-content'] = '<div class="wplnst-content-post wplnst-row-dashicon"><span>'.$post_row.'</span></div>';
398
+
399
+ // Column content for comments
400
+ } elseif ('comments' == $row->object_type) {
401
+
402
+ // Retrieve comment
403
+ $object_id = (int) $row->object_id;
404
+ $comment = get_comment($object_id);
405
+
406
+ // Check comment object
407
+ if (!empty($comment) && is_object($comment)) {
408
+
409
+ // Copy object
410
+ $item['comment'] = $comment;
411
+ $item['can_edit'] = current_user_can('edit_comment', $comment->comment_ID);
412
+
413
+ // Isolate comment author
414
+ $comment_author = ('' === $comment->comment_author)? '' : '<strong>'.esc_html($comment->comment_author).'</strong> &#8212; ';
415
+
416
+ // First comment chars
417
+ $comment_text = wplnst_crop_text($comment->comment_content, 50);
418
+
419
+ // Check editable comment
420
+ if ($item['can_edit']) {
421
+
422
+ // Submitted on link
423
+ $comment_row = '<a href="'.admin_url('comment.php?action=editcomment&c='.$comment->comment_ID).'">'.$comment_author.$comment_text.'</a>';
424
+
425
+ // Check approved comment
426
+ } elseif ('approved' == wp_get_comment_status($comment->comment_ID)) {
427
+
428
+ // View comment
429
+ $comment_row = '<a href="'.esc_url(get_comment_link($comment->comment_ID)).'" target="_blank">'.$comment_author.$comment_text.'</a>';
430
+
431
+ // No link
432
+ } else {
433
+
434
+ // Only text
435
+ $comment_row = $comment_author.$comment_text;
436
+ }
437
+
438
+ // Not found
439
+ } else {
440
+
441
+ // Not found item
442
+ $comment_row = sprintf(__('Comment %d not found', 'wplnst'), $object_id);
443
+ }
444
+
445
+ // Set column value
446
+ $item['wplnst-content'] = '<div class="wplnst-content-comment wplnst-row-dashicon"><span>'.$comment_row.'</span></div>';
447
+
448
+ // Column content for links
449
+ } elseif ('blogroll' == $row->object_type) {
450
+
451
+ // Retrieve link
452
+ $object_id = (int) $row->object_id;
453
+ $bookmark = get_bookmark($object_id);
454
+
455
+ // Check link object
456
+ if (!empty($bookmark) && is_object($bookmark)) {
457
+
458
+ // Copy object
459
+ $item['bookmark'] = $bookmark;
460
+ $item['can_edit'] = current_user_can('manage_links', $bookmark->link_id);
461
+
462
+ // Prepare visible URL
463
+ $link_url = esc_html(url_shorten($bookmark->link_url));
464
+
465
+ // Check editable comment
466
+ if ($item['can_edit']) {
467
+
468
+ // Submitted on link
469
+ $bookmark_row = '<a href="'.admin_url('link.php?action=edit&link_id='.((int) $bookmark->link_id)).'" target="_blank">'.$link_url.'</a>';
470
+
471
+ // No link
472
+ } else {
473
+
474
+ // Only text
475
+ $bookmark_row = '<a href="'.esc_url($link->link_url).'" target="_blank" target="_blank">'.$link_url.'</a>';
476
+ }
477
+
478
+ // Not found
479
+ } else {
480
+
481
+ // Not found item
482
+ $bookmark_row = sprintf(__('Bookmark %d not found', 'wplnst'), $object_id);
483
+ }
484
+
485
+ // Set column value
486
+ $item['wplnst-content'] = '<div class="wplnst-content-bookmark wplnst-row-dashicon"><span>'.$bookmark_row.'</span></div>';
487
+ }
488
+ }
489
+
490
+ // Add row
491
+ $this->items[] = $item;
492
+ }
493
+ }
494
+
495
+
496
+
497
+ // Column actions
498
+ // ---------------------------------------------------------------------------------------------------
499
+
500
+
501
+
502
+ /**
503
+ * Get an associative array ( option_name => option_title ) with the list
504
+ * of bulk actions available on this table.
505
+ *
506
+ * @return array
507
+ */
508
+ protected function get_bulk_actions() {
509
+ return array();
510
+ }
511
+
512
+
513
+
514
+ /**
515
+ * Handles the checkbox column output.
516
+ */
517
+ protected function column_cb($item) {
518
+ if (!$this->results->isolated) {
519
+ $disabled = !(isset($item['post']) || isset($item['comment']) || isset($item['bookmark']));
520
+ return sprintf('<input type="checkbox"'.($disabled? ' disabled' : ' class="wplnst-ck-loc-id"').' value="%s" />', $item['loc_id']);
521
+ }
522
+ }
523
+
524
+
525
+
526
+ /**
527
+ * Default method to display a column
528
+ *
529
+ * @param array $item
530
+ * @param string $column_name
531
+ *
532
+ * @return mixed
533
+ */
534
+ protected function column_default($item, $column_name) {
535
+
536
+ // Actions for URL column
537
+ if ('wplnst-url' == $column_name) {
538
+
539
+ // URL actions row
540
+ $actions = $this->column_actions_url($item);
541
+ if (!empty($actions) && is_array($actions))
542
+ return sprintf('%1$s %2$s', $item[$column_name], $this->row_actions($actions, $item['loc_id']));
543
+
544
+ // Actions for status column
545
+ } elseif ('wplnst-status' == $column_name) {
546
+
547
+ // Status actions row
548
+ $actions = $this->column_actions_status($item);
549
+ if (!empty($actions) && is_array($actions))
550
+ return sprintf('%1$s %2$s', $item[$column_name], $this->row_actions($actions, $item['loc_id']));
551
+
552
+ // Actions for the link anchor
553
+ } elseif ('wplnst-anchor' == $column_name) {
554
+
555
+ // Check links type and available anchor
556
+ if ('links' == $item['link_type'] && false === strpos($item['object_field'], 'custom_field_url_')) {
557
+
558
+ // Anchor actions row
559
+ $actions = $this->column_actions_anchor($item);
560
+ if (!empty($actions) && is_array($actions))
561
+ return sprintf('%1$s %2$s', $item[$column_name], $this->row_actions($actions, $item['loc_id']));
562
+ }
563
+
564
+ // Actions for content column
565
+ } elseif ('wplnst-content' == $column_name) {
566
+
567
+ // Posts actions
568
+ if ('posts' == $item['object_type']) {
569
+
570
+ // Content post actions row
571
+ $actions = $this->column_actions_content_posts($item);
572
+ if (!empty($actions) && is_array($actions))
573
+ return sprintf('%1$s %2$s', $item[$column_name], $this->row_actions($actions, $item['loc_id']));
574
+
575
+ // Comments actions
576
+ } elseif ('comments' == $item['object_type']) {
577
+
578
+ // Content comments actions row
579
+ $actions = $this->column_actions_content_comments($item);
580
+ if (!empty($actions) && is_array($actions))
581
+ return sprintf('%1$s %2$s', $item[$column_name], $this->row_actions($actions, $item['loc_id']));
582
+
583
+ // Blogroll actions
584
+ } elseif ('blogroll' == $item['object_type']) {
585
+
586
+ // Content bookmarks actions row
587
+ $actions = $this->column_actions_content_blogroll($item);
588
+ if (!empty($actions) && is_array($actions))
589
+ return sprintf('%1$s %2$s', $item[$column_name], $this->row_actions($actions, $item['loc_id']));
590
+ }
591
+ }
592
+
593
+ // Default column
594
+ return $item[$column_name];
595
+ }
596
+
597
+
598
+
599
+ /**
600
+ * Column URL row actions
601
+ */
602
+ private function column_actions_url($item) {
603
+
604
+ // Initialize
605
+ $actions = array();
606
+
607
+ // For not isolated
608
+ if (!$this->results->isolated) {
609
+
610
+ // Check object
611
+ if (isset($item['can_edit']) && $item['can_edit']) {
612
+
613
+ // Add results actions
614
+ $actions = apply_filters('wplnst_results_actions_url', $actions, $item);
615
+ }
616
+
617
+ // Filter by URL (for future versions)
618
+ // $actions['wplnst-action-filter'] = '<a href="'.esc_url(WPLNST_Core_Plugin::get_url_scans_locations($this->results->scan->id, $item['url_id'])).'" class="wplnst-results-action">'.__('Filter by URL', 'wplnst').'</a>';
619
+ }
620
+
621
+ // Done
622
+ return $actions;
623
+ }
624
+
625
+
626
+
627
+ /**
628
+ * Column Status row actions
629
+ */
630
+ private function column_actions_status($item) {
631
+
632
+ // Initialize
633
+ $actions = array();
634
+
635
+ // For not isolated
636
+ if (!$this->results->isolated) {
637
+
638
+ // Add results actions
639
+ $actions = apply_filters('wplnst_results_actions_status', $actions, $item);
640
+ }
641
+
642
+ // Done
643
+ return $actions;
644
+ }
645
+
646
+
647
+
648
+ /**
649
+ * Column Anchor row actions
650
+ */
651
+ private function column_actions_anchor($item) {
652
+
653
+ // Initialize
654
+ $actions = array();
655
+
656
+ // For not isolated
657
+ if (!$this->results->isolated) {
658
+
659
+ // Check editable object
660
+ if (isset($item['can_edit']) && $item['can_edit']) {
661
+
662
+ // Add results actions
663
+ $actions = apply_filters('wplnst_results_actions_anchor', $actions, $item);
664
+ }
665
+ }
666
+
667
+ // Done
668
+ return $actions;
669
+ }
670
+
671
+
672
+
673
+ /**
674
+ * Column content row actions for posts
675
+ */
676
+ private function column_actions_content_posts($item) {
677
+
678
+ // Check object
679
+ if (!isset($item['post']))
680
+ return array();
681
+
682
+ // Initialize
683
+ $actions = array();
684
+ $post = $item['post'];
685
+
686
+ // Permissions
687
+ $can_edit_post = $item['can_edit'];
688
+ $post_type_object = get_post_type_object($post->post_type);
689
+
690
+ // Check edit post action
691
+ if ($can_edit_post && 'trash' != $post->post_status)
692
+ $actions['edit'] = '<a href="'.$item['edit_post_link'].'" title="'.esc_attr__('Edit this item').'" target="_blank">'.__( 'Edit' ).'</a>';
693
+
694
+ // Check delete post action
695
+ if (current_user_can('delete_post', $post->ID)) {
696
+
697
+ // Post in trash
698
+ if ('trash' == $post->post_status)
699
+ $actions['untrash'] = "<a title='" . esc_attr__( 'Restore this item from the Trash' ) . "' href='" . wp_nonce_url( admin_url( sprintf( $post_type_object->_edit_link . '&amp;action=untrash', $post->ID ) ), 'untrash-post_' . $post->ID ) . "'>" . __( 'Restore' ) . "</a>";
700
+
701
+ // Post to trash
702
+ elseif (EMPTY_TRASH_DAYS)
703
+ $actions['trash'] = "<a class='submitdelete' title='".esc_attr__('Move this item to the Trash')."' href='".get_delete_post_link($post->ID)."'>".__('Trash')."</a>";
704
+
705
+ // Remove Permanently
706
+ if ('trash' == $post->post_status || !EMPTY_TRASH_DAYS)
707
+ $actions['delete'] = "<a class='submitdelete wplnst-remove-entry' title='".esc_attr__('Delete this item permanently')."' href='".get_delete_post_link($post->ID, '', true)."'>".__('Delete Permanently')."</a>";
708
+ }
709
+
710
+ // Check view post action
711
+ if ($post_type_object->public) {
712
+
713
+ // Post title
714
+ $title = _draft_or_post_title();
715
+
716
+ // Not published
717
+ if (in_array($post->post_status, array('pending', 'draft', 'future'))) {
718
+ if ($can_edit_post) {
719
+ $preview_link = set_url_scheme(get_permalink($post->ID));
720
+ /** This filter is documented in wp-admin/includes/meta-boxes.php */
721
+ $preview_link = apply_filters( 'preview_post_link', add_query_arg('preview', 'true', $preview_link ), $post );
722
+ $actions['view'] = '<a href="' . esc_url( $preview_link ) . '" title="' . esc_attr( sprintf( __( 'Preview &#8220;%s&#8221;' ), $title ) ) . '" rel="permalink" target="_blank">' . __( 'Preview' ) . '</a>';
723
+ }
724
+
725
+ // Not in trash
726
+ } elseif ( 'trash' != $post->post_status ) {
727
+ $actions['view'] = '<a href="' . get_permalink( $post->ID ) . '" title="' . esc_attr( sprintf( __( 'View &#8220;%s&#8221;' ), $title ) ) . '" rel="permalink" target="_blank">' . __( 'View' ) . '</a>';
728
+ }
729
+ }
730
+
731
+ // Done
732
+ return $actions;
733
+ }
734
+
735
+
736
+
737
+ /**
738
+ * Column content row actions for comments
739
+ */
740
+ private function column_actions_content_comments($item) {
741
+
742
+ // Check object
743
+ if (!isset($item['comment']))
744
+ return array();
745
+
746
+ // Initialize
747
+ $actions = array();
748
+ $comment = $item['comment'];
749
+
750
+ // Check editable comment
751
+ if ($item['can_edit']) {
752
+
753
+ // Real comment status
754
+ $the_comment_status = wp_get_comment_status($comment->comment_ID);
755
+
756
+ // Remove and approve nonces
757
+ $del_nonce = esc_html( '_wpnonce=' . wp_create_nonce( "delete-comment_$comment->comment_ID" ) );
758
+ $approve_nonce = esc_html( '_wpnonce=' . wp_create_nonce( "approve-comment_$comment->comment_ID" ) );
759
+
760
+ // Base URL
761
+ $url = "comment.php?c=$comment->comment_ID";
762
+
763
+ // All URLs
764
+ $approve_url = esc_url( $url . "&action=approvecomment&$approve_nonce" );
765
+ $unapprove_url = esc_url( $url . "&action=unapprovecomment&$approve_nonce" );
766
+ $spam_url = esc_url( $url . "&action=spamcomment&$del_nonce" );
767
+ $unspam_url = esc_url( $url . "&action=unspamcomment&$del_nonce" );
768
+ $trash_url = esc_url( $url . "&action=trashcomment&$del_nonce" );
769
+ $untrash_url = esc_url( $url . "&action=untrashcomment&$del_nonce" );
770
+ $delete_url = esc_url( $url . "&action=deletecomment&$del_nonce" );
771
+
772
+ // Preorder it: Edit | Approve | Spam | Trash.
773
+ $actions = array(
774
+ 'edit' => '',
775
+ 'approvecomment' => '', 'unapprove' => '',
776
+ 'spam' => '', 'unspam' => '',
777
+ 'trash' => '', 'untrash' => '', 'delete' => ''
778
+ );
779
+
780
+ if ( 'approved' == $the_comment_status ) {
781
+ $actions['unapprove'] = "<a href='$unapprove_url' data-wp-lists='delete:the-comment-list:comment-$comment->comment_ID:e7e7d3:action=dim-comment&amp;new=unapproved' class='vim-u vim-destructive' title='" . esc_attr__( 'Unapprove this comment' ) . "'>" . __( 'Unapprove' ) . '</a>';
782
+ } elseif ( 'unapproved' == $the_comment_status ) {
783
+ $actions['approvecomment'] = "<a href='$approve_url' data-wp-lists='delete:the-comment-list:comment-$comment->comment_ID:e7e7d3:action=dim-comment&amp;new=approved' class='vim-a vim-destructive' title='" . esc_attr__( 'Approve this comment' ) . "'>" . __( 'Approve' ) . '</a>';
784
+ }
785
+
786
+ if ( 'spam' != $the_comment_status ) {
787
+ $actions['spam'] = "<a href='$spam_url' data-wp-lists='delete:the-comment-list:comment-$comment->comment_ID::spam=1' class='vim-s vim-destructive' title='" . esc_attr__( 'Mark this comment as spam' ) . "'>" . /* translators: mark as spam link */ _x( 'Spam', 'verb' ) . '</a>';
788
+ } elseif ( 'spam' == $the_comment_status ) {
789
+ $actions['unspam'] = "<a href='$unspam_url' data-wp-lists='delete:the-comment-list:comment-$comment->comment_ID:66cc66:unspam=1' class='vim-z vim-destructive'>" . _x( 'Not Spam', 'comment' ) . '</a>';
790
+ }
791
+
792
+ if ( 'trash' == $the_comment_status ) {
793
+ $actions['untrash'] = "<a href='$untrash_url' data-wp-lists='delete:the-comment-list:comment-$comment->comment_ID:66cc66:untrash=1' class='vim-z vim-destructive'>" . __( 'Restore' ) . '</a>';
794
+ }
795
+
796
+ if ( 'spam' == $the_comment_status || 'trash' == $the_comment_status || !EMPTY_TRASH_DAYS ) {
797
+ $actions['delete'] = "<a href='$delete_url' data-wp-lists='delete:the-comment-list:comment-$comment->comment_ID::delete=1' class='delete vim-d vim-destructive wplnst-remove-comment'>" . __( 'Delete Permanently' ) . '</a>';
798
+ } else {
799
+ $actions['trash'] = "<a href='$trash_url' data-wp-lists='delete:the-comment-list:comment-$comment->comment_ID::trash=1' class='delete vim-d vim-destructive' title='" . esc_attr__( 'Move this comment to the trash' ) . "'>" . _x( 'Trash', 'verb' ) . '</a>';
800
+ }
801
+
802
+ if ('spam' != $the_comment_status && 'trash' != $the_comment_status)
803
+ $actions['edit'] = "<a href='comment.php?action=editcomment&amp;c={$comment->comment_ID}' title='" . esc_attr__( 'Edit comment' ) . "'>". __( 'Edit' ) . '</a>';
804
+ }
805
+
806
+ // Check approved comment to see it
807
+ if ('approved' == wp_get_comment_status($comment->comment_ID))
808
+ $actions['view'] = '<a href="'.esc_url(get_comment_link($comment->comment_ID)).'" target="_blank">'.__('View').'</a>';
809
+
810
+ /** This filter is documented in wp-admin/includes/dashboard.php */
811
+ $actions = apply_filters('comment_row_actions', array_filter($actions), $comment);
812
+
813
+ // Done
814
+ return $actions;
815
+ }
816
+
817
+
818
+
819
+ /**
820
+ * Column content row actions for blogroll
821
+ */
822
+ private function column_actions_content_blogroll($item) {
823
+
824
+ // Check object
825
+ if (!isset($item['bookmark']))
826
+ return array();
827
+
828
+ // Initialize
829
+ $actions = array();
830
+ $bookmark = $item['bookmark'];
831
+
832
+ // Check editable comment
833
+ if ($item['can_edit']) {
834
+
835
+ // Cast identifier
836
+ $bookmark_id = (int) $bookmark->link_id;
837
+
838
+ // Edit or remove link
839
+ $actions['edit'] = '<a href="'.admin_url('link.php?action=edit&link_id='.$bookmark_id).'" target="_blank">'.__('Edit').'</a>';
840
+ $actions['trash'] = '<a class="wplnst-remove-bookmark" href="'.wp_nonce_url('link.php?action=delete&link_id='.$bookmark_id, 'delete-bookmark_'.$bookmark_id).'" target="_blank">'.__('Delete').'</a>';
841
+ }
842
+
843
+ // Visit action
844
+ $actions['visit'] = '<a href="'.esc_url($bookmark->link_url).'" target="_blank">'.__('Visit', 'wplnst').'</a>';
845
+
846
+ // Done
847
+ return $actions;
848
+ }
849
+
850
+
851
+
852
+ /**
853
+ * Generate row actions div
854
+ *
855
+ * @since 3.1.0
856
+ * @access protected
857
+ *
858
+ * @param array $actions The list of actions
859
+ * @param bool $always_visible Whether the actions should be always visible
860
+ * @return string
861
+ */
862
+ protected function row_actions( $actions, $loc_id = 0 ) {
863
+
864
+ $action_count = count( $actions );
865
+ $i = 0;
866
+
867
+ if ( !$action_count )
868
+ return '';
869
+
870
+ $out = '<div class="row-actions wplnst-row-actions wplnst-row-actions-'.$loc_id.'">';
871
+ foreach ( $actions as $action => $link ) {
872
+ ++$i;
873
+ ( $i == $action_count ) ? $sep = '' : $sep = ' | ';
874
+ $out .= "<span class='$action'>$link$sep</span>";
875
+ }
876
+ $out .= '</div>';
877
+
878
+ return $out;
879
+ }
880
+
881
+
882
+
883
+ // Display and pagination
884
+ // ---------------------------------------------------------------------------------------------------
885
+
886
+
887
+
888
+ /**
889
+ * Set pagination arguments
890
+ */
891
+ private function setup_pagination() {
892
+ $this->set_pagination_args(array(
893
+ 'per_page' => $this->results->isolated? 0 : $this->results->per_page,
894
+ 'total_items' => $this->results->total_rows,
895
+ 'total_pages' => $this->results->isolated? 0 : $this->results->total_pages,
896
+ ));
897
+ }
898
+
899
+
900
+
901
+ /**
902
+ * Display the table
903
+ *
904
+ * @since 3.1.0
905
+ * @access public
906
+ */
907
+ public function display() {
908
+
909
+ // Wrapper form
910
+ echo '<form method="get" action="'.esc_url(remove_query_arg('paged', set_url_scheme('http://'.$_SERVER['HTTP_HOST'].$_SERVER['REQUEST_URI']))).'" id="wplnst-results" data-nonce="'.esc_attr(wp_create_nonce('wplnst-results-'.$this->results->scan->hash)).'" data-nonce-advanced-display="'.esc_attr(wp_create_nonce('wplnst-results-advanced-display')).'" data-confirm-delete-entry="'.esc_attr__('Please, confirm you want to remove this entry pressing the Ok button', 'wplnst').'" data-confirm-delete-comment="'.esc_attr__('Please, confirm you want to remove this comment pressing the Ok button', 'wplnst').'" data-confirm-delete-bookmark="'.esc_attr__('Please, confirm you want to remove this bookmark pressing the Ok button', 'wplnst').'" data-label-action-url-redir="'.esc_attr__('Apply Redirection', 'wplnst').'" data-label-server-comm-error="'.esc_attr(WPLNST_Core_Text::get_text('server_comm_error')).'" data-label-unknown-error="'.esc_attr(WPLNST_Core_Text::get_text('unknown_error')).'" data-label-select-any="'.esc_attr__('Please, select any result to proceed', 'wplnst').'" data-label-error-code="'.esc_attr__('error code', 'wplnst').'">';
911
+
912
+ // Hidden fields
913
+ echo '<input type="hidden" name="page" value="'.esc_attr($_GET['page']).'" />';
914
+ echo '<input type="hidden" name="scan_id" value="'.esc_attr($this->results->scan->id).'" />';
915
+ echo '<input type="hidden" name="context" value="results" />';
916
+ if (isset($this->results->status_level)) echo '<input type="hidden" name="status" value="'.esc_attr($this->results->status_level).'" />';
917
+
918
+ // Raise event at this point
919
+ do_action('wplnst_scans_results_view_display');
920
+
921
+ // Check isolated classes
922
+ $extra_classes = array();
923
+ if ($this->results->isolated)
924
+ $extra_classes[] = 'wplnst-isolated-table';
925
+
926
+ // Show levels menu
927
+ $this->menu();
928
+
929
+ // Check isolated display
930
+ if (!$this->results->isolated)
931
+ $this->display_tablenav('top');
932
+
933
+ ?><table class="wp-list-table <?php echo implode(' ', array_merge($this->get_table_classes(), $extra_classes)); ?>">
934
+ <thead>
935
+ <tr>
936
+ <?php $this->print_column_headers(); ?>
937
+ </tr>
938
+ </thead>
939
+ <tbody id="the-list">
940
+ <?php $this->display_rows_or_placeholder(); ?>
941
+ </tbody>
942
+ <?php if (!$this->results->isolated) : ?>
943
+ <tfoot>
944
+ <tr>
945
+ <?php $this->print_column_headers( false ); ?>
946
+ </tr>
947
+ </tfoot>
948
+ <?php endif; ?>
949
+ </table><?php
950
+
951
+ // Check isolated display
952
+ if (!$this->results->isolated)
953
+ $this->display_tablenav('bottom');
954
+
955
+ // Close form
956
+ echo '</form>';
957
+ }
958
+
959
+
960
+
961
+ /**
962
+ * Generate the table navigation above or below the table
963
+ */
964
+ protected function display_tablenav($which) {
965
+ ?><div class="tablenav <?php echo esc_attr($which); ?>">
966
+ <?php if ('top' == $which) $this->filters(); ?>
967
+ <?php $this->pagination($which); ?>
968
+ <br class="clear" />
969
+ </div><?php
970
+ }
971
+
972
+
973
+
974
+ // Menu and filters
975
+ // ---------------------------------------------------------------------------------------------------
976
+
977
+
978
+
979
+ /**
980
+ * Display a menu based on status levels
981
+ */
982
+ private function menu() {
983
+
984
+ // Initialize
985
+ $status_levels = WPLNST_Core_Types::get_status_levels();
986
+
987
+ // Enum summary elements
988
+ $levels = array();
989
+ foreach ($this->results->scan->summary as $key => $value) {
990
+
991
+ // Status level record
992
+ if (0 === strpos($key, 'status_level_')) {
993
+ $key = explode('status_level_', $key);
994
+ if (2 == count($key)) {
995
+ $key = $key[1];
996
+ if ('0' == $key) {
997
+ $levels[$key] = (isset($levels[$key])? $levels[$key] : 0) + (int) $value;
998
+ } elseif (isset($status_levels[$key])) {
999
+ $levels[$key] = $value;
1000
+ }
1001
+ }
1002
+ }
1003
+ }
1004
+
1005
+ // Status levels menu
1006
+ $menu_levels = array();
1007
+ foreach ($status_levels as $key => $label) {
1008
+ if (in_array($key, array_keys($levels)))
1009
+ $menu_levels[$key] = $levels[$key];
1010
+ }
1011
+
1012
+ // Check stored total
1013
+ $total = empty($this->results->scan->summary['status_total'])? 0 : (int) $this->results->scan->summary['status_total'];
1014
+
1015
+ // Menu levels
1016
+ $menu_links = array();
1017
+
1018
+ // All results
1019
+ $menu_links[] = '<a href="'.$this->base_url.'"'.($total > 0 && $this->results->is_all_results? ' class="current"' : '').'>'.__('All results', 'wplnst').' </a><span class="count">('.($this->results->is_all_results? number_format_i18n($this->results->total_rows) : number_format_i18n($total)).')</span>';
1020
+
1021
+ // Request error
1022
+ if (!empty($levels['0']))
1023
+ $menu_links[] = '<a href="'.$this->base_url.'&status=0"'.(('0' === $this->results->status_level && !$this->results->is_search)? ' class="current"' : '').'>'.__('Request error', 'wplnst').' </a><span class="count">('.number_format_i18n($levels['0']).')</span>';
1024
+
1025
+ // Level results
1026
+ foreach ($menu_levels as $key => $total)
1027
+ $menu_links[] = '<a href="'.$this->base_url.'&status='.$key.'"'.(($key == $this->results->status_level && !$this->results->is_search)? ' class="current"' : '').'>'.esc_html($key.'00s '.$status_levels[$key]).' </a><span class="count">('.number_format_i18n($total).')</span>';
1028
+
1029
+ // Show menu
1030
+ echo '<div id="wplnst-levels-menu" class="wplnst-clearfix'.($this->results->isolated? ' wplnst-levels-menu-isolated' : '').'"><ul class="subsubsub"><li>'.implode(' | </li><li>', $menu_links).'</li></ul>'.(('end' != $this->results->scan->status)? '<div class="alignright wplnst-aproximate-total">'.__('(counters in progress)', 'wplnst').'</div>' : '').'</div>';
1031
+ }
1032
+
1033
+
1034
+
1035
+ /**
1036
+ * Display a set of filters
1037
+ */
1038
+ protected function filters() {
1039
+
1040
+ // Check filters
1041
+ $fields = $this->filters_fields();
1042
+ if (empty($fields))
1043
+ return;
1044
+
1045
+ // Show menu, actions, etc.
1046
+ echo '<div id="wplnst-results-filters" class="alignleft actions'.$this->filters_classes().'">';
1047
+
1048
+ // Display fields
1049
+ foreach ($fields as $key => $field)
1050
+ echo '<select id="wplnst-filter-'.$key.'"><option value="">'.esc_html($field['title']).'</option>'.$field['options'].'</select>';
1051
+
1052
+ // Button and end of div
1053
+ echo '&nbsp;<input id="wplnst-filter-button" data-fields="'.implode(',', array_keys($fields)).'" data-href="'.esc_attr($this->base_url).'" class="button" type="button" value="'.__('Filter', 'wplnst').'" /></div>';
1054
+ }
1055
+
1056
+
1057
+
1058
+ /**
1059
+ * Additional classes
1060
+ */
1061
+ protected function filters_classes() {
1062
+ return '';
1063
+ }
1064
+
1065
+
1066
+
1067
+ /**
1068
+ * Retrieve fields for basic filters
1069
+ */
1070
+ protected function filters_fields() {
1071
+
1072
+ // Basic fields
1073
+ $fields = array();
1074
+
1075
+
1076
+ /* Status codes filter */
1077
+
1078
+ // Initialize
1079
+ $objects_types = WPLNST_Core_Types::get_objects_types();
1080
+ $status_levels = WPLNST_Core_Types::get_status_levels();
1081
+ $status_codes_raw = WPLNST_Core_Types::get_status_codes_raw();
1082
+
1083
+ // Enum summary elements
1084
+ $levels = $codes = $objects = array();
1085
+ foreach ($this->results->scan->summary as $key => $value) {
1086
+
1087
+ // Status codes
1088
+ if (0 === strpos($key, 'status_code_')) {
1089
+ $key = explode('status_code_', $key);
1090
+ if (2 == count($key)) {
1091
+ $key = $key[1];
1092
+ if ('0' == $key) {
1093
+ $levels['0'] = true;
1094
+ } elseif (3 == strlen($key) && isset($status_codes_raw[$key])) {
1095
+ $level = substr($key, 0, 1);
1096
+ if (isset($status_levels[$level]))
1097
+ $levels[$level] = isset($levels[$level])? $levels[$level] + 1 : 1;
1098
+ $codes[$key] = $value;
1099
+ }
1100
+ }
1101
+
1102
+ // Objects match
1103
+ } elseif (0 === strpos($key, 'objects_match_')) {
1104
+ $key = explode('objects_match_', $key);
1105
+ if (2 == count($key) && in_array($key[1], array_keys($objects_types)))
1106
+ $objects[$key[1]] = $value;
1107
+ }
1108
+ }
1109
+
1110
+
1111
+ /* Status codes options */
1112
+
1113
+ // Collect options
1114
+ $options_codes = empty($levels['0'])? '' : '<option '.((isset($this->results->status_level) && '0' === $this->results->status_level)? 'selected' : '').' value="0">'.__('Request error', 'wplnst').'</option>';
1115
+ $options_levels = array();
1116
+ foreach ($status_codes_raw as $key => $label) {
1117
+ if (isset($codes[$key])) {
1118
+ $level = substr($key, 0, 1);
1119
+ if (isset($levels[$level]) && $levels[$level] > 1 && !in_array($level, $options_levels)) {
1120
+ $options_levels[] = $level;
1121
+ $options_codes .= '<option '.((!empty($this->results->status_level) && $this->results->status_level == $level)? 'selected' : '').' value="'.$level.'">'.$level.'xx'.' '.$status_levels[$level].'</option>';
1122
+ }
1123
+ $options_codes .= '<option '.((!empty($this->results->status_code) && $this->results->status_code == $key)? 'selected' : '').' value="'.$key.'">'.$key.' '.$label.'</option>';
1124
+ }
1125
+ }
1126
+
1127
+ // Add filter
1128
+ $fields['status'] = array(
1129
+ 'type' => 'select',
1130
+ 'title' => __('All status codes', 'wplnst'),
1131
+ 'options' => $options_codes,
1132
+ );
1133
+
1134
+
1135
+ /* Objects options */
1136
+
1137
+ // Collect custom post types
1138
+ $options_post_types = '';
1139
+ if (in_array('posts', array_keys($objects))) {
1140
+ $post_types = WPLNST_Core_Types::get_post_types();
1141
+ foreach ($post_types as $type => $name) {
1142
+ if (in_array($type, $this->results->scan->post_types))
1143
+ $options_post_types .= '<option '.((!empty($this->results->object_post_type) && $this->results->object_post_type == $type)? 'selected' : '').' value="posts_'.$type.'">&mdash;'.esc_html($name).'</option>';
1144
+ }
1145
+ }
1146
+
1147
+ // Collect options
1148
+ $objects_codes = '';
1149
+ foreach ($objects_types as $key => $value) {
1150
+ if (in_array($key, array_keys($objects))) {
1151
+ $objects_codes .= '<option '.((!empty($this->results->object_type) && $this->results->object_type == $key)? 'selected' : '').' value="'.$key.'">'.$value.'</option>';
1152
+ if ('posts' == $key)
1153
+ $objects_codes .= $options_post_types;
1154
+ }
1155
+ }
1156
+
1157
+ // Check filter
1158
+ $fields['otype'] = array(
1159
+ 'type' => 'select',
1160
+ 'title' => __('All content', 'wplnst'),
1161
+ 'options' => $objects_codes,
1162
+ );
1163
+
1164
+
1165
+ /* Options for link types */
1166
+
1167
+ $options_ltypes = '';
1168
+ $link_types = WPLNST_Core_Types::get_link_types();
1169
+ foreach ($link_types as $link_type => $link_type_name)
1170
+ $options_ltypes .= '<option '.((!empty($this->results->link_type) && $this->results->link_type == $link_type)? 'selected' : '').' value="'.esc_attr($link_type).'">'.esc_html($link_type_name).'</option>';
1171
+
1172
+ // Check filter
1173
+ $fields['ltype'] = array(
1174
+ 'type' => 'select',
1175
+ 'title' => __('All link types', 'wplnst'),
1176
+ 'options' => $options_ltypes,
1177
+ );
1178
+
1179
+
1180
+ // Done
1181
+ return $fields;
1182
+ }
1183
+
1184
+
1185
+
1186
+ }
views/scans.php ADDED
@@ -0,0 +1,552 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Check WP constant
4
+ if (!defined('ABSPATH'))
5
+ die;
6
+
7
+ // Check dependencies
8
+ if(!class_exists('WP_List_Table'))
9
+ require_once(ABSPATH.'wp-admin/includes/class-wp-list-table.php');
10
+
11
+ /**
12
+ * WP Link Status Views Scans class
13
+ *
14
+ * @package WP Link Status
15
+ * @subpackage WP Link Status Views
16
+ */
17
+ class WPLNST_Views_Scans extends WP_List_Table {
18
+
19
+
20
+
21
+ // Properties
22
+ // ---------------------------------------------------------------------------------------------------
23
+
24
+
25
+
26
+ /**
27
+ * External data
28
+ */
29
+ private $results;
30
+
31
+
32
+ /**
33
+ * Base URL for filters
34
+ */
35
+ private $base_url;
36
+
37
+
38
+
39
+ // Initialization
40
+ // ---------------------------------------------------------------------------------------------------
41
+
42
+
43
+
44
+ /**
45
+ * Constructor
46
+ */
47
+ public function __construct($results) {
48
+
49
+ // Parent constructor
50
+ parent::__construct();
51
+
52
+ // Copy results
53
+ $this->results = $results;
54
+
55
+ // Base link for filters
56
+ $this->base_url = esc_url(WPLNST_Core_Plugin::get_url_scans());
57
+ }
58
+
59
+
60
+
61
+ /**
62
+ * Prepare columns and data
63
+ */
64
+ function prepare_items() {
65
+
66
+ // Columns
67
+ $this->setup_columns();
68
+
69
+ // Data items
70
+ $this->setup_items();
71
+
72
+ // Pagination
73
+ $this->setup_pagination();
74
+ }
75
+
76
+
77
+
78
+ /**
79
+ * Setup columns
80
+ */
81
+ private function setup_columns() {
82
+
83
+ // Initialize
84
+ $hidden = array();
85
+ $sortable = array();
86
+
87
+ // Column headers
88
+ $this->_column_headers = array($this->get_columns(), $hidden, $sortable);
89
+ }
90
+
91
+
92
+
93
+ /**
94
+ * Columns array
95
+ */
96
+ public function get_columns(){
97
+
98
+ // Checkbox column
99
+ $columns = array();
100
+ if (!$this->results->isolated)
101
+ $columns['cb'] = '<input type="checkbox" />';
102
+
103
+ // All columns
104
+ return array_merge($columns, array(
105
+ 'wplnst-scans-name' => __('Scan info', 'wplnst'),
106
+ 'wplnst-scans-configuration' => __('Configuration', 'wplnst'),
107
+ ));
108
+ }
109
+
110
+
111
+
112
+ /**
113
+ * Setup data items
114
+ */
115
+ private function setup_items() {
116
+
117
+ // Initialize
118
+ $this->items = array();
119
+
120
+ // Warning image
121
+ $warning_img = '<img src="'.plugins_url('assets/images/scan-warning.png', WPLNST_FILE).'" width="16" height="16" border="0" style="margin-right: 5px;" title="'.__('Some critical values of this scan are not completed', 'wplnst').'">';
122
+
123
+ // Populate data
124
+ foreach ($this->results->rows as $scan) {
125
+
126
+ // Initialize
127
+ $timeinfo = false;
128
+ $linksinfo = '';
129
+
130
+ // Normalize identifiers and other data
131
+ $item = array('ID' => $scan->id, 'hash' => $scan->hash, 'status' => $scan->status, 'ready' => $scan->ready);
132
+
133
+ // Check processed posts
134
+ $message = ('play' != $scan->status)? '' : __('Waiting...', 'wplnst');
135
+
136
+ // Initialize
137
+ $processed = array();
138
+ $running_back = false;
139
+ $class_completed = ('end' == $scan->status)? 'wplnst-scan-object-completed-end' : 'wplnst-scan-object-completed';
140
+
141
+ if (isset($scan->trace['total_posts'])) {
142
+ $running_back = empty($scan->trace['populated_posts']);
143
+ $posts_index = empty($scan->trace['posts_index'])? 0 : number_format_i18n($scan->trace['posts_index']);
144
+ $processed[] = empty($scan->trace['populated_posts'])? '<span class="wplnst-scan-object-info wplnst-scan-object-running">'.$posts_index.'/'.number_format_i18n($scan->trace['total_posts']).' '.__('entries', 'wplnst').'</span>' : '<span class="wplnst-scan-object-info '.$class_completed.'">'.number_format_i18n($scan->trace['total_posts']).' '.__('entries', 'wplnst').'</span>';
145
+ }
146
+
147
+ if (isset($scan->trace['total_comments'])) {
148
+ $running = !$running_back && empty($scan->trace['populated_comments']);
149
+ $running_back = $running? true : $running_back;
150
+ $class_running = $running? 'wplnst-scan-object-running' : 'wplnst-scan-object-wait';
151
+ $comments_index = empty($scan->trace['comments_index'])? 0 : number_format_i18n($scan->trace['comments_index']);
152
+ $processed[] = empty($scan->trace['populated_comments'])? '<span class="wplnst-scan-object-info '.$class_running.'">'.$comments_index.'/'.number_format_i18n($scan->trace['total_comments']).' '.__('comments', 'wplnst').'</span>' : '<span class="wplnst-scan-object-info '.$class_completed.'">'.number_format_i18n($scan->trace['total_comments']).' '.__('comments', 'wplnst').'</span>';
153
+ }
154
+
155
+ if (isset($scan->trace['total_blogroll'])) {
156
+ $running = !$running_back && empty($scan->trace['populated_blogroll']);
157
+ $class_running = $running? 'wplnst-scan-object-running' : 'wplnst-scan-object-wait';
158
+ $blogroll_index = empty($scan->trace['blogroll_index'])? 0 : number_format_i18n($scan->trace['blogroll_index']);
159
+ $processed[] = empty($scan->trace['populated_blogroll'])? '<span class="wplnst-scan-object-info '.$class_running.'">'.$blogroll_index.'/'.number_format_i18n($scan->trace['total_blogroll']).' '.__('blogroll', 'wplnst').'</span>' : '<span class="wplnst-scan-object-info '.$class_completed.'">'.number_format_i18n($scan->trace['total_blogroll']).' '.__('blogroll', 'wplnst').'</span>';
160
+ }
161
+
162
+ // Check info
163
+ if (!empty($processed)) {
164
+
165
+ // Processed object types
166
+ $message = implode('&nbsp;', $processed);
167
+
168
+ // Check status
169
+ if ('wait' != $scan->status) {
170
+
171
+
172
+ /* Time info */
173
+
174
+ // Start and local extra time
175
+ $time_start = strtotime($scan->row->started_at.' UTC');
176
+ $offset_time = get_option('gmt_offset') * HOUR_IN_SECONDS;
177
+
178
+ // Current dates
179
+ $today_date = gmdate('d/m/Y', time() + $offset_time);
180
+ $yesterday_date = gmdate('d/m/Y', time() + $offset_time - 86400);
181
+
182
+ // Local date and hour
183
+ $start_date = gmdate('d/m/Y', $time_start + $offset_time);
184
+ $start_hour = gmdate('H:i', $time_start + $offset_time);
185
+
186
+ // Check today
187
+ if ($start_date == $today_date) {
188
+
189
+ // Today
190
+ $started_at = sprintf(__('Today from %s', 'wplnst'), $start_hour);
191
+
192
+ // Check yesterday
193
+ } elseif ($start_date == $yesterday_date) {
194
+
195
+ // Yesterday
196
+ $started_at = sprintf(__('Yesterday at %s', 'wplnst'), $start_hour);
197
+
198
+ // Other
199
+ } else {
200
+
201
+ // Date and hour
202
+ $started_at = sprintf(__('%s at %s', 'wplnst'), $start_date, $start_hour);
203
+ }
204
+
205
+ // Start date
206
+ $timeinfo = $started_at;
207
+
208
+ // Retrieve time stopped
209
+ $time_stopped = isset($scan->summary['time_stopped'])? (int) $scan->summary['time_stopped'] : 0;
210
+
211
+ // Ended scan
212
+ if ('end' == $scan->status) {
213
+
214
+ // Finished date
215
+ $time_end = $time_end_amount = strtotime($scan->row->finished_at.' UTC');
216
+
217
+ // Local date and hour
218
+ $end_date = gmdate('d/m/Y', $time_end + $offset_time);
219
+ $end_hour = gmdate('H:i', $time_end + $offset_time);
220
+
221
+ // Check today
222
+ if ($end_date == $today_date) {
223
+
224
+ // All today
225
+ if ($start_date == $today_date) {
226
+
227
+ // Started and finished today
228
+ $timeinfo .= ' '.sprintf(__('to %s', 'wplnst'), $end_hour);
229
+
230
+ // Date to today
231
+ } else {
232
+
233
+ // Started another date and finished today
234
+ $timeinfo .= ' '.sprintf(__('to today at %s', 'wplnst'), $end_hour);
235
+ }
236
+
237
+ // Check yesterday
238
+ } elseif ($end_date == $yesterday_date) {
239
+
240
+ // All yesterday
241
+ if ($start_date == $yesterday_date) {
242
+
243
+ // Started and finished today
244
+ $timeinfo .= ' '.sprintf(__('to %s', 'wplnst'), $end_hour);
245
+
246
+ // Date to yesterday
247
+ } else {
248
+
249
+ // Started another date and finished today
250
+ $timeinfo .= ' '.sprintf(__('to yesterday at %s', 'wplnst'), $end_hour);
251
+ }
252
+
253
+ // Check same day
254
+ } elseif ($end_date == $start_date) {
255
+
256
+ // Same day
257
+ $timeinfo .= ' '.sprintf(__('until %s', 'wplnst'), $end_hour);
258
+
259
+ // Before
260
+ } else {
261
+
262
+ // Different days
263
+ $timeinfo .= ' '.sprintf(__('until %s at %s', 'wplnst'), $end_date, $end_hour);
264
+ }
265
+
266
+ // Time stopped correction
267
+ if (!empty($time_stopped))
268
+ $time_end_amount -= $time_stopped;
269
+
270
+ // Total time
271
+ $timeinfo .= ' &#8212; '.human_time_diff($time_start, $time_end_amount);
272
+
273
+ // Running
274
+ } else {
275
+
276
+ // Until now or stopped
277
+ $time_end = ('stop' == $scan->status)? strtotime($scan->row->stopped_at.' UTC') : time();
278
+
279
+ // Time stopped correction
280
+ if (!empty($time_stopped))
281
+ $time_end -= $time_stopped;
282
+
283
+ // Elapsed time
284
+ $timeinfo .= ' &#8212; '.sprintf(__('Running time %s', 'wplnst'), human_time_diff($time_start, $time_end));
285
+ }
286
+
287
+
288
+ /* Links info */
289
+
290
+ // Normalize
291
+ $phases = array();
292
+ foreach ($scan->summary as $key => $value) {
293
+ if (0 === strpos($key, 'urls_phase_')) {
294
+ $phase = explode('urls_phase_', $key);
295
+ $phases[$phase[1]] = $value;
296
+ }
297
+ }
298
+
299
+ // Check values
300
+ if (!empty($phases)) {
301
+
302
+ // Collect data
303
+ $items = array();
304
+
305
+ // All results
306
+ $items[] = (isset($scan->summary['status_total']) && $scan->summary['status_total'] > 0)? sprintf(__('<strong>%s</strong> results', 'wplnst'), number_format_i18n((int) $scan->summary['status_total'])) : __('No results', 'wplnst');
307
+
308
+ // Total processed
309
+ if (!empty($phases['processed'])) {
310
+
311
+ // Add uniques
312
+ $items[] = sprintf(__('<strong>%s</strong> unique URLs', 'wplnst'), number_format_i18n((int) $phases['processed']));
313
+ }
314
+
315
+ // Enqueued URLs
316
+ if ('end' != $scan->status)
317
+ $items[] = sprintf(__('%s enqueued', 'wplnst'), (isset($phases['wait'])? number_format_i18n((int) $phases['wait']) : '0'));
318
+
319
+ // Waiting
320
+ if ('play' == $scan->status)
321
+ $items[] = sprintf(__('%s processing', 'wplnst'), (empty($phases['play'])? wplnst_get_nsetting('max_threads', $scan->threads->max) : (int) $phases['play']));
322
+
323
+ // End crawler
324
+ if ('end' == $scan->status)
325
+ $items[] = sprintf(__('<strong>%s</strong> Request error', 'wplnst'), (isset($scan->summary['status_level_0'])? (int) number_format_i18n($scan->summary['status_level_0']) : '0'));
326
+
327
+ // Join items
328
+ if (!empty($items))
329
+ $linksinfo = implode('<span class="wplnst-split-char">&middot;</span>', $items);
330
+ }
331
+ }
332
+
333
+ // Only created
334
+ } elseif ('wait' == $scan->status || 'queued' == $scan->status || 'stop' == $scan->status) {
335
+
336
+ // Created date
337
+ $time_created = strtotime($scan->row->created_at.' UTC');
338
+ $offset_time = get_option('gmt_offset') * HOUR_IN_SECONDS;
339
+
340
+ // Current dates
341
+ $today_date = gmdate('d/m/Y', time() + $offset_time);
342
+ $yesterday_date = gmdate('d/m/Y', time() + $offset_time - 86400);
343
+
344
+ // Local date and hour
345
+ $created_date = gmdate('d/m/Y', $time_created + $offset_time);
346
+ $created_hour = gmdate('H:i', $time_created + $offset_time);
347
+
348
+ // Check today
349
+ if ($created_date == $today_date) {
350
+
351
+ // Created today
352
+ $timeinfo = sprintf(__('Created today at %s', 'wplnst'), $created_hour);
353
+
354
+ // Yesterday
355
+ } elseif ($created_date == $yesterday_date) {
356
+
357
+ // Created yesterday
358
+ $timeinfo = sprintf(__('Created yesterday at %s', 'wplnst'), $created_hour);
359
+
360
+ // Any date
361
+ } else {
362
+
363
+ // Created date and hour
364
+ $timeinfo = sprintf(__('Created %s at %s', 'wplnst'), $created_date, $created_hour);
365
+ }
366
+ }
367
+
368
+
369
+
370
+ // Item name, possible warning and link to edit
371
+ $statuses = WPLNST_Core_Types::get_scan_statuses();
372
+ $item['wplnst-scans-name'] = '<strong class="wplnst-scan-name'.($scan->ready? '' : ' wplnst-scan-name-warning').'">'.($scan->ready? '' : $warning_img);
373
+ if ('wait' != $scan->status)
374
+ $item['wplnst-scans-name'] .= '<span class="wplnst-scan-status wplnst-scan-status-'.esc_attr($scan->status).'">'.$statuses[$scan->status].'</span>&nbsp;';
375
+ $item['wplnst-scans-name'] .= '<a class="row-title" href="'.esc_url(WPLNST_Core_Plugin::get_url_scans_results($scan->id)).'">'.(empty($scan->name)? __('(no name)', 'wplnst') : esc_html($scan->name)).'</a></strong>';
376
+ $item['wplnst-scans-name'] .= '<div class="wplnst-scan-status-line">'.$message.'</div>';
377
+
378
+ // Check ready message
379
+ if ('wait' == $scan->status && $scan->ready)
380
+ $item['wplnst-scans-name'] .= '<div class="wplnst-scan-ready-info">'.sprintf(__('Ready to <a href="%s">start the crawler</a>', 'wplnst'), esc_url(WPLNST_Core_Plugin::get_url_scans_crawler($scan->id, 'on', $scan->hash))).'</div>';
381
+
382
+ // Check datatime info
383
+ if (!empty($timeinfo))
384
+ $item['wplnst-scans-name'] .= '<div class="wplnst-scan-time-info">'.$timeinfo.'</div>';
385
+
386
+ // Check links info
387
+ if (!empty($linksinfo))
388
+ $item['wplnst-scans-name'] .= '<div class="wplnst-scan-links-info">'.$linksinfo.'</div>';
389
+
390
+
391
+ // Prepare configuration
392
+ $item['wplnst-scans-configuration'] = '<table>';
393
+
394
+ // Scan scope
395
+ $link_types = (empty($scan->link_types_names)? '' : implode(', ', array_map('esc_html', $scan->link_types_names)));
396
+ $item['wplnst-scans-configuration'] .= '<tr><td class="wplnst-scans-configuration-row"><strong>'.__('Scope', 'wplnst').'</strong></td><td>'.(empty($link_types)? '' : $link_types.', ').esc_html($scan->destination_type_name).', '.esc_html($scan->time_scope_name).', '.esc_html(__('Order by', 'wplnst')).' '.esc_html($scan->crawl_order_name).', '.($scan->redir_status? __('Check redirection status', 'wplnst') : __('Redirections not checked', 'wplnst')).($scan->malformed? ', '.__('Malformed', 'wplnst') : '').'</td></tr>';
397
+
398
+ // Post types and status
399
+ $item['wplnst-scans-configuration'] .= (empty($scan->post_types_names) && empty($scan->post_status_names))? '' : '<tr><td class="wplnst-scans-configuration-row"><strong>'.__('Post types', 'wplnst').'</strong></td><td>'.(empty($scan->post_types_names)? '' : implode(', ', array_map('esc_html', $scan->post_types_names))).(empty($scan->post_status_names)? '' : (empty($scan->post_types_names)? '' : ', ').__('Post status', 'wplnst').' '.implode(', ', array_map('esc_html', $scan->post_status_names))).'</td></tr>';
400
+
401
+ // Links status
402
+ $item['wplnst-scans-configuration'] .= empty($scan->links_status_names)? '' : '<tr><td class="wplnst-scans-configuration-row"><strong>'.__('Link status', 'wplnst').'</strong></td><td>'.implode(', ', array_map('esc_html', $scan->links_status_names)).'</td></tr>';
403
+
404
+ // End configuration
405
+ $item['wplnst-scans-configuration'] .= '</table>';
406
+
407
+ // Add row
408
+ $this->items[] = $item;
409
+ }
410
+ }
411
+
412
+
413
+
414
+ /**
415
+ * Default method to display a column
416
+ *
417
+ * @param array $item
418
+ * @param string $column_name
419
+ *
420
+ * @return mixed
421
+ */
422
+ protected function column_default($item, $column_name) {
423
+
424
+ // Actions for name column
425
+ if ('wplnst-scans-name' == $column_name) {
426
+
427
+ // Initialize
428
+ $actions = array();
429
+
430
+ // Results link
431
+ $actions['results'] = '<a href="'.esc_url(WPLNST_Core_Plugin::get_url_scans_results($item['ID'])).'">'.__('Show results', 'wplnst').'</a>';
432
+
433
+ // Edit link
434
+ $actions['edit'] = '<span class="edit"><a href="'.esc_url(WPLNST_Core_Plugin::get_url_scans_edit($item['ID'])).'">'.__('Edit scan', 'wplnst').'</a></span>';
435
+
436
+ // Stop or start crawler
437
+ if ('end' != $item['status'] && ($item['ready'] || 'play' == $item['status']))
438
+ $actions['crawler'] = '<a href="'.esc_url(WPLNST_Core_Plugin::get_url_scans_crawler($item['ID'], ('play' == $item['status'] || 'queued' == $item['status'])? 'off' : 'on', $item['hash'])).'">'.(in_array($item['status'], array('wait', 'stop'))? __('Start crawler', 'wplnst') : (('queued' == $item['status'])? __('Unqueue crawling', 'wplnst') : __('Stop crawler', 'wplnst'))).'</a>';
439
+
440
+ // Remove scan
441
+ $actions['delete'] = '<span class="trash"><a href="'.esc_url(WPLNST_Core_Plugin::get_url_scans_delete($item['ID'], $item['hash'])).'" class="wplnst-scan-delete">'.esc_html(WPLNST_Admin::get_text('scan_delete')).'</a></span>';
442
+
443
+ // Done
444
+ return sprintf('%1$s %2$s', $item[$column_name], $this->row_actions($actions));
445
+ }
446
+
447
+ // Default column
448
+ return $item[$column_name];
449
+ }
450
+
451
+
452
+
453
+ /**
454
+ * Get an associative array ( option_name => option_title ) with the list
455
+ * of bulk actions available on this table.
456
+ *
457
+ * @return array
458
+ */
459
+ protected function get_bulk_actions() {
460
+ return array(
461
+ 'delete' => __('Delete', 'wplnst'),
462
+ );
463
+ }
464
+
465
+
466
+
467
+ /**
468
+ * Handles the checkbox column output.
469
+ */
470
+ protected function column_cb($item) {
471
+ return $this->results->isolated? null : sprintf('<input type="checkbox" class="wplnst-ck-scan-id" value="%s" />', $item['ID']);
472
+ }
473
+
474
+
475
+
476
+ /**
477
+ * Set pagination arguments
478
+ */
479
+ private function setup_pagination() {
480
+ $this->set_pagination_args(array(
481
+ 'per_page' => $this->results->isolated? 0 : (isset($this->results->per_page)? $this->results->per_page : 1),
482
+ 'total_items' => isset($this->results->total_rows)? $this->results->total_rows : count($this->results->rows),
483
+ 'total_pages' => $this->results->isolated? 0 : (isset($this->results->total_pages)? $this->results->total_pages : 1),
484
+ ));
485
+ }
486
+
487
+
488
+
489
+ /**
490
+ * Display the table
491
+ *
492
+ * @since 3.1.0
493
+ * @access public
494
+ */
495
+ public function display() {
496
+
497
+ // Wrapper form
498
+ echo '<form method="get" action="'.esc_url($this->base_url).'" id="wplnst-scans" data-href-delete="'.WPLNST_Core_Plugin::get_url_scans_delete('%scan_id%', 'bulk-scans-delete').'" data-confirm-delete="'.esc_attr(WPLNST_Admin::get_text('scan_delete_confirm')).'" data-confirm-delete-bulk="'.esc_attr__('Do you want to remove these scans?', 'wplnst').'">';
499
+
500
+ // Check isolated classes
501
+ $extra_classes = array();
502
+ if ($this->results->isolated)
503
+ $extra_classes[] = 'wplnst-isolated-table';
504
+
505
+ // Check isolated display
506
+ if (!$this->results->isolated)
507
+ $this->display_tablenav('top');
508
+
509
+ ?><table class="wp-list-table <?php echo implode(' ', array_merge($this->get_table_classes(), $extra_classes)); ?>">
510
+ <thead>
511
+ <tr>
512
+ <?php $this->print_column_headers(); ?>
513
+ </tr>
514
+ </thead>
515
+ <tbody id="the-list">
516
+ <?php $this->display_rows_or_placeholder(); ?>
517
+ </tbody>
518
+ <?php if (!$this->results->isolated) : ?>
519
+ <tfoot>
520
+ <tr>
521
+ <?php $this->print_column_headers( false ); ?>
522
+ </tr>
523
+ </tfoot>
524
+ <?php endif; ?>
525
+ </table><?php
526
+
527
+ // Check isolated display
528
+ if (!$this->results->isolated)
529
+ $this->display_tablenav('bottom');
530
+
531
+ // Close form
532
+ echo '</form>';
533
+ }
534
+
535
+
536
+
537
+ /**
538
+ * Generate the table navigation above or below the table
539
+ */
540
+ protected function display_tablenav($which) {
541
+ ?><div class="tablenav <?php echo esc_attr($which); ?>">
542
+ <div class="alignleft actions bulkactions wplnst-scans-bulkactions-<?php echo $which; ?>">
543
+ <?php $this->bulk_actions($which); ?>
544
+ </div>
545
+ <?php $this->pagination($which); ?>
546
+ <br class="clear" />
547
+ </div><?php
548
+ }
549
+
550
+
551
+
552
+ }
views/settings.php ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ // Load views class
4
+ require_once(dirname(__FILE__).'/views.php');
5
+
6
+ /**
7
+ * WP Link Status Views Settings class
8
+ *
9
+ * @package WP Link Status
10
+ * @subpackage WP Link Status Views
11
+ */
12
+ class WPLNST_Views_Settings extends WPLNST_Views {
13
+
14
+
15
+
16
+ /**
17
+ * Show scan edit form
18
+ */
19
+ public static function view($args) {
20
+
21
+ // Vars
22
+ extract($args);
23
+
24
+ ?><form method="post" id="wplnst-form" action="<?php echo esc_url($action); ?>">
25
+
26
+ <input type="hidden" name="settings_nonce" value="<?php echo esc_attr($nonce); ?>" />
27
+
28
+ <h2 id="wplnst-tabs-nav" class="nav-tab-wrapper">
29
+ <a id="wplnst-crawling-tab" href="#top#wplnst-crawling" class="nav-tab"><?php _e('Crawling', 'wplnst'); ?></a>
30
+ <a id="wplnst-timing-tab" href="#top#wplnst-timing" class="nav-tab"><?php _e('Timing', 'wplnst'); ?></a>
31
+ <a id="wplnst-advanced-tab" href="#top#wplnst-advanced" class="nav-tab"><?php _e('Advanced', 'wplnst'); ?></a>
32
+ </h2>
33
+
34
+ <div id="wplnst-tabs">
35
+
36
+ <div id="wplnst-crawling" class="wplnst-tab wplnst-tab-default">
37
+
38
+ <table class="form-table">
39
+ <tr>
40
+ <th><label for="tx-max-threads"><?php _e('Number of crawler threads', 'wplnst'); ?></label></th>
41
+ <td class="wplnst-col-input"><input type="text" name="tx-max-threads" id="tx-max-threads" value="<?php echo esc_attr(wplnst_get_nsetting('max_threads')); ?>" maxlength="3" class="small-text" /></td>
42
+ <td class="wplnst-col-info"><?php _e('One thread means an HTTP request to your site only used for crawling purposes.', 'wplnst'); ?></td>
43
+ </tr>
44
+ <tr>
45
+ <th><label for="tx-max-scans"><?php _e('Max crawlers running', 'wplnst'); ?></label></th>
46
+ <td class="wplnst-col-input"><input type="text" name="tx-max-scans" id="tx-max-scans" value="<?php echo esc_attr(wplnst_get_nsetting('max_scans')); ?>" maxlength="3" class="small-text" /></td>
47
+ <td class="wplnst-col-info"><?php _e('Number of crawlers allowed to run simultaneously, each one with its own threads.', 'wplnst'); ?></td>
48
+ </tr>
49
+ <tr>
50
+ <th><label for="tx-max-pack"><?php _e('Max pack items', 'wplnst'); ?></label></th>
51
+ <td class="wplnst-col-input"><input type="text" name="tx-max-pack" id="tx-max-pack" value="<?php echo esc_attr(wplnst_get_nsetting('max_pack')); ?>" maxlength="3" class="small-text" /></td>
52
+ <td class="wplnst-col-info"><?php _e('Total objects (posts, comments or blogroll) processed in one single thread.', 'wplnst'); ?></td>
53
+ </tr>
54
+ <tr>
55
+ <th><label for="tx-max-scans"><?php _e('Max URL request attempts', 'wplnst'); ?></label></th>
56
+ <td class="wplnst-col-input"><input type="text" name="tx-max-requests" id="tx-max-requests" value="<?php echo esc_attr(wplnst_get_nsetting('max_requests')); ?>" maxlength="3" class="small-text" /></td>
57
+ <td class="wplnst-col-info"><?php _e('Number of HTTP requests attempts before set an URL as wrong.', 'wplnst'); ?></td>
58
+ </tr>
59
+ <tr>
60
+ <th><label for="tx-max-redirs"><?php _e('Max redirections allowed', 'wplnst'); ?></label></th>
61
+ <td class="wplnst-col-input"><input type="text" name="tx-max-redirs" id="tx-max-redirs" value="<?php echo esc_attr(wplnst_get_nsetting('max_redirs')); ?>" maxlength="3" class="small-text" /></td>
62
+ <td class="wplnst-col-info"><?php _e('Total redirections steps allowed to follow from original URL.', 'wplnst'); ?></td>
63
+ </tr>
64
+ <tr>
65
+ <th><label for="tx-max-download"><?php _e('Max download size', 'wplnst'); ?></label></th>
66
+ <td class="wplnst-col-input"><input type="text" name="tx-max-download" id="tx-max-download" value="<?php echo esc_attr(wplnst_get_nsetting('max_download')); ?>" maxlength="5" class="small-text" /></td>
67
+ <td class="wplnst-col-info"><?php _e('KB', 'wplnst'); ?> &nbsp; <?php echo sprintf(__('(minimum value of %s KB and max value of %s KB).', 'wplnst'), number_format_i18n(wplnst_get_nsetting('max_download', 'min')), number_format_i18n(wplnst_get_nsetting('max_download', 'max'))); ?></td>
68
+ </tr>
69
+ <tr>
70
+ <th><label for="tx-user-agent"><?php _e('Default User Agent', 'wplnst'); ?></label></th>
71
+ <td colspan="3" class="wplnst-col-input"><input type="text" name="tx-user-agent" id="tx-user-agent" value="<?php echo esc_attr(wplnst_get_tsetting('user_agent')); ?>" maxlength="255" class="regular-text" style="width: 75%;" /></td>
72
+ </tr>
73
+ </table>
74
+
75
+ </div>
76
+
77
+ <div id="wplnst-timing" class="wplnst-tab">
78
+
79
+ <table class="form-table">
80
+ <tr>
81
+ <th><label for="tx-connect-timeout"><?php _e('URL Connection timeout', 'wplnst'); ?></label></th>
82
+ <td class="wplnst-col-input"><input type="text" name="tx-connect-timeout" id="tx-connect-timeout" value="<?php echo esc_attr(wplnst_get_nsetting('connect_timeout')); ?>" maxlength="3" class="small-text" /></td>
83
+ <td class="wplnst-col-info"><?php _e('seconds', 'wplnst'); ?>. <?php _e('Max time allowed to establish a connection with the URL host.', 'wplnst'); ?></td>
84
+ </tr>
85
+ <tr>
86
+ <th><label for="tx-request-timeout"><?php _e('URL Request timeout', 'wplnst'); ?></label></th>
87
+ <td class="wplnst-col-input"><input type="text" name="tx-request-timeout" id="tx-request-timeout" value="<?php echo esc_attr(wplnst_get_nsetting('request_timeout')); ?>" maxlength="3" class="small-text" /></td>
88
+ <td class="wplnst-col-info"><?php _e('seconds', 'wplnst'); ?>. <?php _e('Max time allowed to retrieve headers and body from one URL.', 'wplnst'); ?></td>
89
+ </tr>
90
+ <tr>
91
+ <th><label for="tx-extra-timeout"><?php _e('URL Extra timeout check', 'wplnst'); ?></label></th>
92
+ <td class="wplnst-col-input"><input type="text" name="tx-extra-timeout" id="tx-extra-timeout" value="<?php echo esc_attr(wplnst_get_nsetting('extra_timeout')); ?>" maxlength="3" class="small-text" /></td>
93
+ <td class="wplnst-col-info"><?php _e('seconds', 'wplnst'); ?> <?php printf(__('(mininum value of %d seconds)', 'wplnst'), wplnst_get_nsetting('extra_timeout', 'min')); ?>. <?php _e('A little grace period to avoid timeouts conflicts.', 'wplnst'); ?></td>
94
+ </tr>
95
+ <tr>
96
+ <th><label for="tx-crawler-alive"><?php _e('Check crawler alive each', 'wplnst'); ?></label></th>
97
+ <td class="wplnst-col-input"><input type="text" name="tx-crawler-alive" id="tx-crawler-alive" value="<?php echo esc_attr(wplnst_get_nsetting('crawler_alive')); ?>" maxlength="3" class="small-text" /></td>
98
+ <td class="wplnst-col-info"><?php _e('seconds', 'wplnst'); ?> <?php printf(__('(mininum value of %d seconds)', 'wplnst'), wplnst_get_nsetting('crawler_alive', 'min')); ?>. <?php _e('Checks if a crawler is interrupted and if so restart it.', 'wplnst'); ?></td>
99
+ </tr>
100
+ <tr>
101
+ <th><label for="tx-total-objects"><?php _e('Total objects check each', 'wplnst'); ?></label></th>
102
+ <td class="wplnst-col-input"><input type="text" name="tx-total-objects" id="tx-total-objects" value="<?php echo esc_attr(wplnst_get_nsetting('total_objects')); ?>" maxlength="3" class="small-text" /></td>
103
+ <td class="wplnst-col-info"><?php _e('seconds', 'wplnst'); ?> <?php printf(__('(mininum value of %d seconds)', 'wplnst'), wplnst_get_nsetting('total_objects', 'min')); ?>. <?php _e('Total of objects (posts, comments or blogroll) to check links.', 'wplnst'); ?></td>
104
+ </tr>
105
+ <tr>
106
+ <th><label for="tx-summary-status"><?php _e('Summary of status each', 'wplnst'); ?></label></th>
107
+ <td class="wplnst-col-input"><input type="text" name="tx-summary-status" id="tx-summary-status" value="<?php echo esc_attr(wplnst_get_nsetting('summary_status')); ?>" maxlength="3" class="small-text" /></td>
108
+ <td class="wplnst-col-info"><?php _e('seconds', 'wplnst'); ?> <?php printf(__('(mininum value of %d seconds)', 'wplnst'), wplnst_get_nsetting('summary_status', 'min')); ?>. <?php _e('Calculate status code totals to display data in real time.', 'wplnst'); ?></td>
109
+ </tr>
110
+ <tr>
111
+ <th><label for="tx-summary-phases"><?php _e('Summary of URLs each', 'wplnst'); ?></label></th>
112
+ <td><input type="text" name="tx-summary-phases" id="tx-summary-phases" value="<?php echo esc_attr(wplnst_get_nsetting('summary_phases')); ?>" maxlength="3" class="small-text" /></td>
113
+ <td class="wplnst-col-info"><?php _e('seconds', 'wplnst'); ?> <?php printf(__('(mininum value of %d seconds)', 'wplnst'), wplnst_get_nsetting('summary_phases', 'min')); ?>. <?php _e('Current number of URLs processed or waiting to be checked.', 'wplnst'); ?></td>
114
+ </tr>
115
+ <tr>
116
+ <th><label for="tx-summary-objects"><?php _e('Summary of objects each', 'wplnst'); ?></label></th>
117
+ <td><input type="text" name="tx-summary-objects" id="tx-summary-objects" value="<?php echo esc_attr(wplnst_get_nsetting('summary_objects')); ?>" maxlength="3" class="small-text" /></td>
118
+ <td class="wplnst-col-info"><?php _e('seconds', 'wplnst'); ?> <?php printf(__('(mininum value of %d seconds)', 'wplnst'), wplnst_get_nsetting('summary_objects', 'min')); ?>. <?php _e('Summary of objects (posts, comments or blogroll) with processed URLs.', 'wplnst'); ?></td>
119
+ </tr>
120
+ </table>
121
+
122
+ </div>
123
+
124
+
125
+ <div id="wplnst-advanced" class="wplnst-tab">
126
+
127
+ <table class="form-table">
128
+ <tr>
129
+ <th><label for="tx-recursion-limit"><?php _e('Recursion limit', 'wplnst'); ?></label></th>
130
+ <td colspan="2"><input type="text" name="tx-recursion-limit" id="tx-recursion-limit" value="<?php echo esc_attr(wplnst_get_nsetting('recursion_limit')); ?>" maxlength="5" class="small-text" /> <?php _e('function calls', 'wplnst'); ?></td>
131
+ </tr>
132
+ <tr>
133
+ <th><?php _e('Data results pagination', 'wplnst'); ?></th>
134
+ <td colspan="2" class="wplnst-list"><input type="checkbox" <?php echo wplnst_get_bsetting('mysql_calc_rows')? 'checked' : ''; ?> name="ck-mysql-calc-rows" id="ck-mysql-calc-rows" value="on" /><label for="ck-mysql-calc-rows">&nbsp;<?php _e('Use <code>SQL_CALC_FOUND_ROWS</code> to calculate total rows.', 'wplnst'); ?></label></td>
135
+ </tr>
136
+ <tr>
137
+ <th><?php _e('Data on uninstall', 'wplnst'); ?></th>
138
+ <td colspan="2" class="wplnst-list"><input type="checkbox" <?php echo wplnst_get_bsetting('uninstall_data')? 'checked' : ''; ?> name="ck-uninstall-data" id="ck-uninstall-data" value="on" /><label for="ck-uninstall-data">&nbsp;<?php _e('Options and exclusive MySQL tables will be removed when uninstall this plugin.', 'wplnst'); ?></label></td>
139
+ </tr>
140
+ </table>
141
+
142
+ </div>
143
+
144
+
145
+ <p><input type="submit" value="<?php _e('Save settings', 'wplnst'); ?>" class="button button-primary" /></p>
146
+
147
+ </div>
148
+
149
+ </form><?php
150
+ }
151
+
152
+
153
+
154
+ }
views/views.php ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * WP Link Status Views class
5
+ *
6
+ * @package WP Link Status
7
+ * @subpackage WP Link Status Views
8
+ */
9
+ abstract class WPLNST_Views {
10
+
11
+
12
+
13
+ /**
14
+ * Compose select option values
15
+ */
16
+ public static function options($options, $values, $display = true, $space = false) {
17
+ $inner = '';
18
+ foreach ($options as $key => $name)
19
+ $inner .= '<option'.(self::selected($key, $values, false)? ' selected' : '').' value="'.esc_attr($key).'">'.esc_html($name).'</option>';
20
+ if ($display)
21
+ echo $inner;
22
+ return $inner;
23
+ }
24
+
25
+
26
+
27
+ /**
28
+ * Check a checked value
29
+ */
30
+ public static function checked($current, $values, $display = true) {
31
+ $checked = self::is_value($current, $values);
32
+ if ($checked && $display)
33
+ echo 'checked';
34
+ return $checked;
35
+ }
36
+
37
+
38
+
39
+ /**
40
+ * Check a selected value
41
+ */
42
+ public static function selected($current, $values, $display = true, $space = false) {
43
+ $selected = self::is_value($current, $values);
44
+ if ($selected && $display)
45
+ echo ($space? ' ' : '').'selected';
46
+ return $selected;
47
+ }
48
+
49
+
50
+
51
+ /**
52
+ * Check if value match an array of values
53
+ */
54
+ public static function is_value($current, $values) {
55
+ if (empty($values) && false !== $values)
56
+ return false;
57
+ return is_array($values)? in_array($current, $values) : ($current == $values);
58
+ }
59
+
60
+
61
+
62
+ /**
63
+ * Encode and sanitize a json list
64
+ */
65
+ public static function esc_attr_elist($json) {
66
+ $json_new = array();
67
+ foreach ($json as $json_item) {
68
+ $json_item_new = array();
69
+ foreach ($json_item as $key => $value)
70
+ $json_item_new[esc_attr($key)] = esc_attr($value);
71
+ $json_new[] = $json_item_new;
72
+ }
73
+ return str_replace('&quot;', '%quot%', @json_encode($json_new));
74
+ }
75
+
76
+
77
+
78
+ }
wp-link-status.php ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /*
3
+ Plugin Name: WP Broken Link Status Checker
4
+ Plugin URI: http://seedplugins.com/wp-link-status/
5
+ Description: Check and manage HTTP response codes of all your content site links and images.
6
+ Version: 1.0
7
+ Author: SeedPlugins
8
+ License: GPLv2 or later
9
+ Text Domain: wplnst
10
+ Domain Path: /languages
11
+ */
12
+
13
+ // Avoid script calls via plugin URL
14
+ if (!function_exists('add_action'))
15
+ die;
16
+
17
+ // Boot checks
18
+ require(dirname(__FILE__).'/core/boot.php');
19
+
20
+ // This plugin constants
21
+ define('WPLNST_FILE', __FILE__);
22
+ define('WPLNST_PATH', dirname(WPLNST_FILE));
23
+ define('WPLNST_VERSION', '1.0');
24
+
25
+ // Check scan crawling action
26
+ require_once(WPLNST_PATH.'/core/alive.php');
27
+ WPLNST_Core_Alive::check();
28
+
29
+ // Check context
30
+ if (is_admin()) {
31
+
32
+ // Admin area
33
+ wplnst_require('admin', 'admin');
34
+ WPLNST_Admin::instantiate();
35
+
36
+ // Plugin activation
37
+ register_activation_hook(WPLNST_FILE, 'wplnst_plugin_activation');
38
+ function wplnst_plugin_activation() {
39
+ wplnst_require('core', 'register');
40
+ WPLNST_Core_Register::activation();
41
+ }
42
+
43
+ // Plugin deactivation
44
+ register_deactivation_hook(WPLNST_FILE, 'wplnst_plugin_deactivation');
45
+ function wplnst_plugin_deactivation() {
46
+ wplnst_require('core', 'register');
47
+ WPLNST_Core_Register::deactivation();
48
+ }
49
+
50
+ // Plugin uninstall
51
+ register_uninstall_hook(WPLNST_FILE, 'wplnst_plugin_uninstall');
52
+ function wplnst_plugin_uninstall() {
53
+ wplnst_require('core', 'register');
54
+ WPLNST_Core_Register::uninstall();
55
+ }
56
+ }