WP RSS Aggregator - Version 4.17

Version Description

(2019-12-11) =

Added - New "Tools" that replaces the "Blacklist", "Import/Export" and "Debugging" pages. - New option to control whether items with future dates are scheduled or published with truncated dates. - New "feeds" shortcode parameter to select feed sources by their slug names. - New "1 week" update interval option to update feeds once every week. - The "Edit Feed Source" page now allows the slug to be edited. - The "Edit Feed Source" page now shows shortcode snippets.

Changed - RSS feeds that are invalid due to leading whitespace are now trimmed and may now be imported. - Images that have the same URL are now downloaded to the media library only once. - Updated some styles to match the new WordPress 5.3 aesthetic. - Optimized template saving to be more performant and less error prone. - Improved error messages in REST API responses. - Removed some log messages. - Fatal errors are now always logged. - Optimized cron-related functionality. - The plugin will no longer register cron schedules that already exist. - License-related notices are now only shown to users who have access to the Licenses settings page.

Fixed - The "Import Source" option did not work. - Templates now link imported posts to the local post instead of to the original article. - Images with HTML entities in the URL could not be downloaded. - Feed items without a PolyLang translation did not show up in templates. - PHP notices were triggered when trying to download invalid images. - The feed item count in the "Feed Sources" page would show zero when certain add-ons are installed. - Removed a warning shown in templates about reset() expecting an array. - Thumbnails imported by Excerpts & Thumbnails were not shown in templates. - Some databases would report the following error during logging: "Column 'date' cannot be null". - Unserializing the options for the system info triggered PHP notices.

Download this release

Release Info

Developer Mekku
Plugin Icon 128x128 WP RSS Aggregator
Version 4.17
Comparing to
See all releases

Code changes from version 4.16 to 4.17

Files changed (90) hide show
  1. CHANGELOG.md +36 -0
  2. css/admin-styles.css +203 -108
  3. includes/Aventura/Wprss/Core/Component/AdminAjaxNotices.php +1 -1
  4. includes/Aventura/Wprss/Core/Licensing/Settings.php +12 -0
  5. includes/Aventura/Wprss/Core/Model/AdminAjaxNotice/ServiceProvider.php +1 -1
  6. includes/admin-debugging.php +0 -22
  7. includes/admin-display.php +2 -2
  8. includes/admin-help-metaboxes.php +1 -1
  9. includes/admin-help-settings.php +5 -0
  10. includes/admin-import-export.php +0 -245
  11. includes/admin-metaboxes.php +18 -16
  12. includes/admin-options.php +20 -5
  13. includes/admin.php +1 -10
  14. includes/cron-jobs.php +282 -203
  15. includes/di.php +14 -4
  16. includes/fallback-mbstring.php +3 -3
  17. includes/feed-access.php +18 -4
  18. includes/feed-blacklist.php +129 -276
  19. includes/feed-importing-images.php +126 -117
  20. includes/feed-importing.php +27 -28
  21. includes/feed-processing.php +21 -18
  22. includes/functions.php +42 -0
  23. includes/image-caching.php +9 -5
  24. includes/libraries/browser.php +1 -17
  25. includes/scripts.php +15 -0
  26. includes/system-info.php +90 -136
  27. includes/update.php +3 -0
  28. js/admin-debug.js +0 -7
  29. js/beacon.min.js +7 -3
  30. js/blacklist-tool.js +19 -0
  31. js/logs-tool.js +11 -0
  32. js/reset-tool.js +24 -0
  33. js/tools.js +198 -0
  34. lib/Entities/Properties/AliasProperty.php +54 -0
  35. readme.txt +31 -8
  36. src/Entities/Collections/FeedBlacklistCollection.php +54 -0
  37. src/Entities/Collections/FeedItemCollection.php +14 -0
  38. src/Entities/Collections/WpEntityCollection.php +8 -1
  39. src/Entities/Properties/WpFtImageUrlProperty.php +22 -4
  40. src/Entities/Properties/WpPostPermalinkProperty.php +53 -0
  41. src/Entities/Properties/WpraItemSourceProperty.php +94 -0
  42. src/Entities/Properties/WpraPostTypeDependentProperty.php +100 -0
  43. src/Entities/Stores/WpPostStore.php +1 -1
  44. src/ErrorHandler.php +36 -7
  45. src/Handlers/EchoHandler.php +40 -0
  46. src/Handlers/FeedBlacklist/SaveBlacklistHandler.php +0 -70
  47. src/Handlers/Images/RenderItemsImageColumnHandler.php +3 -5
  48. src/Handlers/RegisterSubMenuPageHandler.php +12 -2
  49. src/Handlers/RenderMetaBoxTemplateHandler.php +69 -0
  50. src/Logger/WpdbLogger.php +7 -1
  51. src/Modules/BlacklistToolModule.php +221 -0
  52. src/Modules/BulkAddToolModule.php +93 -0
  53. src/Modules/FeedBlacklistModule.php +48 -8
  54. src/Modules/FeedItemsModule.php +26 -8
  55. src/Modules/FeedSourcesModule.php +33 -0
  56. src/Modules/ImportExportToolsModule.php +177 -0
  57. src/Modules/LoggerModule.php +0 -83
  58. src/Modules/LogsToolModule.php +157 -0
  59. src/Modules/ResetToolModule.php +150 -0
  60. src/Modules/SysInfoToolModule.php +98 -0
  61. src/Modules/ToolsModule.php +140 -0
  62. src/RestApi/EndPoints/AbstractRestApiEndPoint.php +40 -7
  63. src/Templates/Feeds/MasterFeedsTemplate.php +2 -2
  64. src/Templates/Feeds/Types/AbstractWpraFeedTemplateType.php +8 -2
  65. src/Ui/BlacklistTable.php +204 -0
  66. src/Util/{SanitizeIdCommaListCapableTrait.php → SanitizeCommaListCapableTrait.php} +21 -7
  67. templates/admin/debug/log.twig +0 -121
  68. templates/admin/feeds/save-meta-box.twig +12 -0
  69. templates/admin/feeds/shortcode.twig +19 -0
  70. templates/admin/tools/blacklist.twig +47 -0
  71. templates/admin/tools/bulk_add.twig +16 -0
  72. templates/admin/tools/export.twig +16 -0
  73. templates/admin/tools/import.twig +16 -0
  74. templates/admin/tools/logs.twig +119 -0
  75. templates/admin/tools/main.twig +28 -0
  76. templates/admin/tools/reset.twig +32 -0
  77. templates/admin/tools/sys_info.twig +19 -0
  78. test/bootstrap.php +0 -21
  79. vendor/autoload.php +1 -1
  80. vendor/composer/autoload_classmap.php +16 -2
  81. vendor/composer/autoload_real.php +7 -7
  82. vendor/composer/autoload_static.php +21 -7
  83. vendor/composer/installed.json +7 -7
  84. vendor/psr/log/Psr/Log/LoggerInterface.php +2 -0
  85. vendor/psr/log/Psr/Log/LoggerTrait.php +2 -0
  86. vendor/psr/log/Psr/Log/NullLogger.php +2 -0
  87. vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.php +3 -1
  88. vendor/psr/log/Psr/Log/Test/TestLogger.php +1 -0
  89. vendor/psr/log/composer.json +1 -1
  90. wp-rss-aggregator.php +17 -10
CHANGELOG.md CHANGED
@@ -4,6 +4,42 @@ All notable changes to this project will be documented in this file.
4
  The format is based on [Keep a Changelog](http://keepachangelog.com/)
5
  and this project adheres to [Semantic Versioning](http://semver.org/).
6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  ## [4.16] - 2019-10-31
8
  ### Changed
9
  * Overhauled the data set system with a more robust entity system.
4
  The format is based on [Keep a Changelog](http://keepachangelog.com/)
5
  and this project adheres to [Semantic Versioning](http://semver.org/).
6
 
7
+ ## [4.17] - 2019-12-11
8
+ ### Added
9
+ * New "Tools" that replaces the "Blacklist", "Import/Export" and "Debugging" pages.
10
+ * New option to control whether items with future dates are scheduled or published with truncated dates.
11
+ * New "feeds" shortcode parameter to select feed sources by their slug names.
12
+ * New "1 week" update interval option to update feeds once every week.
13
+ * The "Edit Feed Source" page now allows the slug to be edited.
14
+ * The "Edit Feed Source" page now shows shortcode snippets.
15
+
16
+ ### Changed
17
+ * RSS feeds that are invalid due to leading whitespace are now trimmed and may now be imported.
18
+ * Images that have the same URL are now downloaded to the media library only once.
19
+ * Updated some styles to match the new WordPress 5.3 aesthetic.
20
+ * Optimized template saving to be more performant and less error prone.
21
+ * Improved error messages in REST API responses.
22
+ * Removed some log messages.
23
+ * Fatal errors are now always logged.
24
+ * Optimized cron-related functionality.
25
+ * The plugin will no longer register cron schedules that already exist.
26
+ * License-related notices are now only shown to users who have access to the Licenses settings page.
27
+
28
+ ### Fixed
29
+ * The "Import Source" option did not work.
30
+ * Templates now link imported posts to the local post instead of to the original article.
31
+ * Images with HTML entities in the URL could not be downloaded.
32
+ * Feed items without a PolyLang translation did not show up in templates.
33
+ * PHP notices were triggered when trying to download invalid images.
34
+ * The feed item count in the "Feed Sources" page would show zero when certain add-ons are installed.
35
+ * Removed a warning shown in templates about `reset()` expecting an array.
36
+ * Thumbnails imported by Excerpts & Thumbnails were not shown in templates.
37
+ * Some databases would report the following error during logging: "Column 'date' cannot be null".
38
+ * Unserializing the options for the system info triggered PHP notices.
39
+
40
+ ### Removed
41
+ * A WordPress 5.1 function was being used to check for ready cron jobs, now replaced with a custom function.
42
+
43
  ## [4.16] - 2019-10-31
44
  ### Changed
45
  * Overhauled the data set system with a more robust entity system.
css/admin-styles.css CHANGED
@@ -27,7 +27,7 @@ span.wp-rss-footer-text {
27
  float: left;
28
  width: 95px !important;
29
  }
30
-
31
  .wprss-input input {
32
  height: 28px;
33
  margin-bottom: 0;
@@ -109,7 +109,7 @@ div#force-feed-container {
109
 
110
 
111
 
112
- body.post-type-wprss_feed_item .add-new-h2,
113
  body.post-type-wprss_feed_item .tablenav select[name="m"],
114
  body.post-type-wprss_feed_item #post-query-submit
115
  { display: none; }
@@ -148,7 +148,7 @@ color: darkGreen;
148
  color: red;
149
  }
150
 
151
- #preview_meta_box .rss-date {
152
  color: #666;
153
 
154
  }
@@ -167,9 +167,9 @@ color: darkGreen;
167
  input#default-thumbnail,
168
  input#wprss-et-license-key,
169
  input#wprss-c-license-key,
170
- input#wprss-kf-license-key,
171
- input#wprss-ftp-license-key {
172
- width: 300px;
173
  }
174
 
175
  input#thumbnails-height,
@@ -181,79 +181,46 @@ input#thumbnails-width {
181
  /* Red Button */
182
 
183
  .wp-core-ui .button-red {
184
- background-color: #9B2124;
185
- background-image: -webkit-gradient(linear, left top, left bottom, from(#C5292E), to(#9B2124));
186
- background-image: -webkit-linear-gradient(top, #C5292E, #9B2124);
187
- background-image: -moz-linear-gradient(top, #C5292E, #9B2124);
188
- background-image: -ms-linear-gradient(top, #C5292E, #9B2124);
189
- background-image: -o-linear-gradient(top, #C5292E, #9B2124);
190
- background-image: linear-gradient(to bottom, #C5292E, #9B2124);
191
- border-color: #9B2124;
192
- border-bottom-color: #8D1F21;
193
- -webkit-box-shadow: inset 0 1px 0 rgba(120,200,230,0.5);
194
- box-shadow: inset 0 1px 0 rgba(120,200,230,0.5);
195
  color: #fff;
196
  text-decoration: none;
197
- text-shadow: 0 1px 0 rgba(0,0,0,0.1);
198
- /*float: right;*/
199
  }
200
 
201
  .wp-core-ui .button-red.hover,
202
- .wp-core-ui .button-red:hover,
203
- .wp-core-ui .button-red.focus,
204
- .wp-core-ui .button-red:focus {
205
- background-color: #B72629;
206
- background-image: -webkit-gradient(linear, left top, left bottom, from(#D22E30), to(#9B2124));
207
- background-image: -webkit-linear-gradient(top, #D22E30, #9B2124);
208
- background-image: -moz-linear-gradient(top, #D22E30, #9B2124);
209
- background-image: -ms-linear-gradient(top, #D22E30, #9B2124);
210
- background-image: -o-linear-gradient(top, #D22E30, #9B2124);
211
- background-image: linear-gradient(to bottom, #D22E30, #9B2124);
212
- border-color: #7F1C1F;
213
- -webkit-box-shadow: inset 0 1px 0 rgba(120,200,230,0.6);
214
- box-shadow: inset 0 1px 0 rgba(120,200,230,0.6);
215
  color: #fff;
216
- text-shadow: 0 -1px 0 rgba(0,0,0,0.3);
 
217
  }
218
 
219
  .wp-core-ui .button-red.focus,
220
  .wp-core-ui .button-red:focus {
221
- border-color: #500F0E;
222
- -webkit-box-shadow: inset 0 1px 0 rgba(120,200,230,0.6), 1px 1px 2px rgba(0,0,0,0.4);
223
- box-shadow: inset 0 1px 0 rgba(120,200,230,0.6), 1px 1px 2px rgba(0,0,0,0.4);
 
224
  }
225
 
226
  .wp-core-ui .button-red.active,
227
- .wp-core-ui .button-red.active:hover,
228
- .wp-core-ui .button-red.active:focus,
229
  .wp-core-ui .button-red:active {
230
- background: #7F1C1F;
231
- background-image: -webkit-gradient(linear, left top, left bottom, from(#9B2124), to(#B72629));
232
- background-image: -webkit-linear-gradient(top, #9B2124, #B72629);
233
- background-image: -moz-linear-gradient(top, #9B2124, #B72629);
234
- background-image: -ms-linear-gradient(top, #9B2124, #B72629);
235
- background-image: -o-linear-gradient(top, #9B2124, #B72629);
236
- background-image: linear-gradient(to bottom, #9B2124, #B72629);
237
- border-color: #601312 #AE2426 #AE2426 #AE2426;
238
- color: rgba(255,255,255,0.95);
239
- -webkit-box-shadow: inset 0 1px 0 rgba(0,0,0,0.1);
240
- box-shadow: inset 0 1px 0 rgba(0,0,0,0.1);
241
- text-shadow: 0 1px 0 rgba(0,0,0,0.1);
242
  }
243
 
244
  .wp-core-ui .button-red[disabled],
245
  .wp-core-ui .button-red:disabled,
246
  .wp-core-ui .button-red-disabled {
247
- color: #E79496 !important;
248
- background: #BA292B !important;
249
- border-color: #7F1C1F !important;
250
- -webkit-box-shadow: none !important;
251
- box-shadow: none !important;
252
- text-shadow: 0 -1px 0 rgba(0,0,0,0.1) !important;
253
- cursor: default;
254
  }
255
 
256
- #system-info-textarea,
257
  #wprss-error-log-textarea {
258
  width: 800px;
259
  height: 400px;
@@ -263,13 +230,25 @@ input#thumbnails-width {
263
  display: block;
264
  }
265
 
 
 
 
 
 
 
 
 
 
266
  .wprss-error-log-action {
267
  display: inline-block;
268
- margin: 15px 5px 15px 0;
269
  }
270
 
271
- #wprss-download-error-log-form {
272
- float: right;
 
 
 
 
273
  }
274
 
275
  form.wprss-error-log-action input[type="submit"]:active {
@@ -277,8 +256,9 @@ form.wprss-error-log-action input[type="submit"]:active {
277
  }
278
  /* The debug log root element */
279
  div.wpra-log {
280
- display: none;
281
- width: 800px;
 
282
  }
283
 
284
  /* The "Filters" label */
@@ -288,7 +268,7 @@ div.wpra-log > p > strong:first-child {
288
  }
289
 
290
  /* The debug log filters separator */
291
- div.wpra-log .wpra-toggle-logs:not(:last-child)::after {
292
  content: ' | ';
293
  color: #3c3c3c;
294
  pointer-events: none;
@@ -339,9 +319,11 @@ div.wpra-log .wpra-toggle-logs.wpra-log-filter-disabled a {
339
 
340
  /* The debug log */
341
  div.wpra-log-container {
 
342
  max-height: 600px;
343
  border: 1px solid #d1d1d1;
344
  padding: 5px;
 
345
  overflow: auto;
346
  background: #222833;
347
  }
@@ -350,9 +332,11 @@ div.wpra-log-container {
350
  div.wpra-log-container,
351
  .notice.wpra-empty-log-notice {
352
  display: block;
353
- width: 800px;
354
  box-sizing: border-box;
355
  }
 
 
 
356
 
357
  /* The log table */
358
  div.wpra-log-container > table {
@@ -368,8 +352,8 @@ div.wpra-log-container > table > tbody > tr {
368
  div.wpra-log-container > table > tbody > tr > td {
369
  color: #cbcdda;
370
  vertical-align: baseline;
371
- padding: 1px 10px;
372
- border-bottom: 1px solid transparent;
373
  }
374
 
375
  /* Log table: row on hover */
@@ -391,7 +375,7 @@ div.wpra-log-container > table > tbody > tr > td.wpra-log-level {
391
  width: 60px;
392
  }
393
  div.wpra-log-container > table > tbody > tr > td.wpra-log-feed {
394
- width: 80px;
395
  }
396
 
397
  /* Log table: styling for DEBUG log messages */
@@ -419,43 +403,6 @@ div.wpra-log-container > table > tbody > tr.wpra-log-error > td {
419
  font-weight: bold;
420
  }
421
 
422
- #wprss-error-log-options {
423
- display: none;
424
- margin: 10px 0;
425
- padding: 10px;
426
- background: #fff;
427
- border: 1px solid #ccc;
428
- border-radius: 2px;
429
- }
430
-
431
- #wprss-error-log-options form {
432
- display: block;
433
- }
434
-
435
- #wprss-error-log-options label {
436
- display: inline-block;
437
- line-height: 1;
438
- font-size: 1em;
439
- margin: 3px 0;
440
- }
441
-
442
- #wprss-error-log-options label > span:first-child {
443
- display: inline-block;
444
- font-weight: bold;
445
- width: 200px;
446
- }
447
-
448
- #wprss-log-options-form input[name="logging_limit_days"] {
449
- width: 80px;
450
- }
451
-
452
- #wpra-log-options-submit-wrap {
453
- text-align: right;
454
- }
455
- #wpra-log-options-submit-wrap > button {
456
- text-align: center;
457
- }
458
-
459
  @media screen and (min-width: 782px) {
460
  #custom_feed_title {
461
  width: 400px;
@@ -484,7 +431,7 @@ div.wpra-log-container > table > tbody > tr.wpra-log-error > td {
484
  input#thumbnails-height,
485
  input#thumbnails-width {
486
  margin-right: 2px;
487
- }
488
 
489
  label[for=thumbnails-height],
490
  label[for=thumbnails-width] {
@@ -721,13 +668,13 @@ i.wprss-updating-feed-icon.wprss-show {
721
  #add-ons .add-on-group {
722
  margin-top: 20px;
723
  padding-top: 20px;
724
- border-top: #F5F5F5 solid 1px;
725
  }
726
 
727
  #add-ons .add-on-group:first-child {
728
  margin-top: 0;
729
  padding-top: 0;
730
- border-top: 0 none;
731
  }
732
 
733
  #add-ons .add-on {
@@ -1165,3 +1112,151 @@ input:checked + .wprss-switch-slider:before {
1165
  -ms-transform: translateX(14px);
1166
  transform: translateX(14px);
1167
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
  float: left;
28
  width: 95px !important;
29
  }
30
+
31
  .wprss-input input {
32
  height: 28px;
33
  margin-bottom: 0;
109
 
110
 
111
 
112
+ body.post-type-wprss_feed_item .add-new-h2,
113
  body.post-type-wprss_feed_item .tablenav select[name="m"],
114
  body.post-type-wprss_feed_item #post-query-submit
115
  { display: none; }
148
  color: red;
149
  }
150
 
151
+ #preview_meta_box .rss-date {
152
  color: #666;
153
 
154
  }
167
  input#default-thumbnail,
168
  input#wprss-et-license-key,
169
  input#wprss-c-license-key,
170
+ input#wprss-kf-license-key,
171
+ input#wprss-ftp-license-key {
172
+ width: 300px;
173
  }
174
 
175
  input#thumbnails-height,
181
  /* Red Button */
182
 
183
  .wp-core-ui .button-red {
184
+ background: #BD2B2B;
185
+ border-color: transparent;
 
 
 
 
 
 
 
 
 
186
  color: #fff;
187
  text-decoration: none;
188
+ text-shadow: none;
 
189
  }
190
 
191
  .wp-core-ui .button-red.hover,
192
+ .wp-core-ui .button-red:hover {
 
 
 
 
 
 
 
 
 
 
 
 
193
  color: #fff;
194
+ border-color: transparent;
195
+ background-color: #AC0F0F;
196
  }
197
 
198
  .wp-core-ui .button-red.focus,
199
  .wp-core-ui .button-red:focus {
200
+ color: #fff;
201
+ background-color: #AC0F0F;
202
+ border-color: transparent;
203
+ box-shadow: 0 0 0 1px #fff,0 0 0 3px #AC0F0F;
204
  }
205
 
206
  .wp-core-ui .button-red.active,
 
 
207
  .wp-core-ui .button-red:active {
208
+ color: #fff;
209
+ background-color: #A20909;
210
+ border-color: transparent;
 
 
 
 
 
 
 
 
 
211
  }
212
 
213
  .wp-core-ui .button-red[disabled],
214
  .wp-core-ui .button-red:disabled,
215
  .wp-core-ui .button-red-disabled {
216
+ color: #fba6a8 !important;
217
+ background: #DD383A !important;
218
+ box-shadow: 0 0 0 transparent !important;
219
+ border-color: transparent !important;
220
+ text-shadow: 0 0 0 transparent !important;
 
 
221
  }
222
 
223
+ #wpra-system-info-textarea,
224
  #wprss-error-log-textarea {
225
  width: 800px;
226
  height: 400px;
230
  display: block;
231
  }
232
 
233
+ #wpra-system-info-textarea {
234
+ display: block;
235
+ width: 100%;
236
+ height: 600px;
237
+ font-family: Menlo, Monaco, monospace;
238
+ white-space: pre;
239
+ overflow: auto;
240
+ }
241
+
242
  .wprss-error-log-action {
243
  display: inline-block;
 
244
  }
245
 
246
+ #wprss-clear-error-log-form {
247
+ display: inline-block;
248
+ }
249
+ #wprss-clear-error-log-form,
250
+ #wprss-clear-error-log-form > button {
251
+ vertical-align: baseline;
252
  }
253
 
254
  form.wprss-error-log-action input[type="submit"]:active {
256
  }
257
  /* The debug log root element */
258
  div.wpra-log {
259
+ display: block;
260
+ position: relative;
261
+ width: 100%;
262
  }
263
 
264
  /* The "Filters" label */
268
  }
269
 
270
  /* The debug log filters separator */
271
+ div.wpra-log .wpra-toggle-logs:not(:last-of-type)::after {
272
  content: ' | ';
273
  color: #3c3c3c;
274
  pointer-events: none;
319
 
320
  /* The debug log */
321
  div.wpra-log-container {
322
+ width: 100%;
323
  max-height: 600px;
324
  border: 1px solid #d1d1d1;
325
  padding: 5px;
326
+ margin-bottom: 15px;
327
  overflow: auto;
328
  background: #222833;
329
  }
332
  div.wpra-log-container,
333
  .notice.wpra-empty-log-notice {
334
  display: block;
 
335
  box-sizing: border-box;
336
  }
337
+ .notice.wpra-empty-log-notice {
338
+ width: 400px;
339
+ }
340
 
341
  /* The log table */
342
  div.wpra-log-container > table {
352
  div.wpra-log-container > table > tbody > tr > td {
353
  color: #cbcdda;
354
  vertical-align: baseline;
355
+ padding: 4px 10px;
356
+ border-bottom: 2px solid #222833;
357
  }
358
 
359
  /* Log table: row on hover */
375
  width: 60px;
376
  }
377
  div.wpra-log-container > table > tbody > tr > td.wpra-log-feed {
378
+ width: 150px;
379
  }
380
 
381
  /* Log table: styling for DEBUG log messages */
403
  font-weight: bold;
404
  }
405
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
406
  @media screen and (min-width: 782px) {
407
  #custom_feed_title {
408
  width: 400px;
431
  input#thumbnails-height,
432
  input#thumbnails-width {
433
  margin-right: 2px;
434
+ }
435
 
436
  label[for=thumbnails-height],
437
  label[for=thumbnails-width] {
668
  #add-ons .add-on-group {
669
  margin-top: 20px;
670
  padding-top: 20px;
671
+ border-top: #F5F5F5 solid 1px;
672
  }
673
 
674
  #add-ons .add-on-group:first-child {
675
  margin-top: 0;
676
  padding-top: 0;
677
+ border-top: 0 none;
678
  }
679
 
680
  #add-ons .add-on {
1112
  -ms-transform: translateX(14px);
1113
  transform: translateX(14px);
1114
  }
1115
+
1116
+ .postbox .inside .wpra-feed-save-meta-box {
1117
+ border-bottom: 1px solid #ccc;
1118
+ padding: 10px;
1119
+ margin: 0 -10px;
1120
+ margin-top: -11px;
1121
+ margin-bottom: 10px;
1122
+ background: #fff;
1123
+ }
1124
+
1125
+ .postbox .inside .wpra-feed-save-meta-box label span {
1126
+ font-weight: bold;
1127
+ font-size: 14px;
1128
+ }
1129
+
1130
+ .postbox .inside .wpra-feed-save-meta-box input[type="text"] {
1131
+ text-align: left !important;
1132
+ }
1133
+
1134
+ /* -- INLINE STYLES -- */
1135
+
1136
+ form.wpra-inline-form {
1137
+ display: inline-block;
1138
+ }
1139
+
1140
+ form.wpra-header-form {
1141
+ display: inline-block;
1142
+ margin: 0;
1143
+ padding: 0;
1144
+ border: 0;
1145
+ }
1146
+
1147
+ h1.wpra-inline-header,
1148
+ h2.wpra-inline-header,
1149
+ h3.wpra-inline-header,
1150
+ h4.wpra-inline-header
1151
+ {
1152
+ display: inline-block;
1153
+ margin-right: 5px;
1154
+ }
1155
+
1156
+ button.button.wpra-header-button {
1157
+ position: relative;
1158
+ vertical-align: baseline;
1159
+ top: -1px;
1160
+ }
1161
+
1162
+ /* -- SLIDE BOX -- */
1163
+
1164
+ div.wpra-slide-box {
1165
+ display: none;
1166
+ margin: 10px 0;
1167
+ padding: 15px;
1168
+ background: #fff;
1169
+ border: 1px solid #ccc;
1170
+ border-radius: 2px;
1171
+ }
1172
+
1173
+ div.wpra-slide-box.wpra-slide-box-open {
1174
+ display: block;
1175
+ }
1176
+
1177
+ div.wpra-slide-box form {
1178
+ display: block;
1179
+ }
1180
+
1181
+ div.wpra-slide-box h4 {
1182
+ font-size: 16px;
1183
+ margin: 0;
1184
+ margin-bottom: 15px;
1185
+ padding-bottom: 5px;
1186
+ border-bottom: 1px solid #ddd;
1187
+ }
1188
+
1189
+ div.wpra-slide-box form label {
1190
+ display: flex;
1191
+ flex-direction: row;
1192
+ align-items: center;
1193
+ line-height: 1;
1194
+ font-size: 1em;
1195
+ margin: 5px 0;
1196
+ }
1197
+
1198
+ div.wpra-slide-box label > * {
1199
+ flex-grow: 1;
1200
+ }
1201
+
1202
+ div.wpra-slide-box label > span {
1203
+ display: inline-block;
1204
+ flex-grow: 0;
1205
+ width: 200px;
1206
+ font-size: 1em;
1207
+ }
1208
+ div.wpra-slide-box label input[type="text"],
1209
+ div.wpra-slide-box label input[type="number"],
1210
+ div.wpra-slide-box label input[type="text"]
1211
+ {
1212
+ height: 28px;
1213
+ min-height: 28px;
1214
+ }
1215
+
1216
+ div.wpra-slide-box div.wpra-submit-wrap {
1217
+ margin-top: 10px;
1218
+ }
1219
+ div.wpra-slide-box div.wpra-submit-wrap.right {
1220
+ text-align: right;
1221
+ }
1222
+
1223
+ div.wpra-slide-box div.wpra-submit-wrap > button {
1224
+ text-align: center;
1225
+ }
1226
+
1227
+ /* -- ERROR LOG OPTIONS (SLIDE BOX) -- */
1228
+
1229
+ #wprss-log-options-form input[name="logging_limit_days"] {
1230
+ width: 80px;
1231
+ }
1232
+
1233
+ /* -- BLACKLIST TOOL -- */
1234
+
1235
+ #wpra-add-blacklist-container form label span:first-child {
1236
+ width: 120px;
1237
+ }
1238
+
1239
+ /* -- LOG TOOL -- */
1240
+
1241
+ #wprss-error-log-options {
1242
+ margin: 15px 0;
1243
+ }
1244
+
1245
+ #wprss-clear-error-log-form {
1246
+ float: right;
1247
+ position: relative;
1248
+ top: -3px;
1249
+ }
1250
+
1251
+ .wpra-log-filters {
1252
+ padding: 2px 0;
1253
+ margin: 0;
1254
+ }
1255
+ p.wpra-logs-shown-msg {
1256
+ margin: 5px 0;
1257
+ }
1258
+
1259
+ /* -- MISC -- */
1260
+ .wpra-no-margin {
1261
+ margin: 0;
1262
+ }
includes/Aventura/Wprss/Core/Component/AdminAjaxNotices.php CHANGED
@@ -204,7 +204,7 @@ class AdminAjaxNotices extends Core\Plugin\ComponentAbstract
204
  *
205
  * @see wprss_admin_notice_add()
206
  *
207
- * @param array $notice Data of the notice
208
  *
209
  * @return bool|WP_Error True if notice added, false if collection unavailable, or WP_Error if something went wrong.
210
  */
204
  *
205
  * @see wprss_admin_notice_add()
206
  *
207
+ * @param array|string $notice Data of the notice
208
  *
209
  * @return bool|WP_Error True if notice added, false if collection unavailable, or WP_Error if something went wrong.
210
  */
includes/Aventura/Wprss/Core/Licensing/Settings.php CHANGED
@@ -109,6 +109,10 @@ class Settings {
109
  * @return boolean True if the notice is to be shown, false if not.
110
  */
111
  public function emptyLicenseKeyNoticeCondition( $args ) {
 
 
 
 
112
  if ( ! isset( $args['addon'] ) ) {
113
  return false;
114
  }
@@ -129,6 +133,10 @@ class Settings {
129
  * @return boolean True if the notice is to be shown, false if not.
130
  */
131
  public function savedInactiveLicenseNoticeCondition( $args ) {
 
 
 
 
132
  if ( ! isset( $args['addon'] ) ) {
133
  return false;
134
  }
@@ -143,6 +151,10 @@ class Settings {
143
  * @return boolean True if the notice is to be shown, false if not.
144
  */
145
  public function soonToExpireLicenseNoticeCondition( $args ) {
 
 
 
 
146
  if ( ! isset( $args['addon'] ) ) return false;
147
  $manager = $this->getManager();
148
  if ( !($license = $manager->getLicense( $args['addon'] )) ) {
109
  * @return boolean True if the notice is to be shown, false if not.
110
  */
111
  public function emptyLicenseKeyNoticeCondition( $args ) {
112
+ if (!current_user_can('manage_options')) {
113
+ return false;
114
+ }
115
+
116
  if ( ! isset( $args['addon'] ) ) {
117
  return false;
118
  }
133
  * @return boolean True if the notice is to be shown, false if not.
134
  */
135
  public function savedInactiveLicenseNoticeCondition( $args ) {
136
+ if (!current_user_can('manage_options')) {
137
+ return false;
138
+ }
139
+
140
  if ( ! isset( $args['addon'] ) ) {
141
  return false;
142
  }
151
  * @return boolean True if the notice is to be shown, false if not.
152
  */
153
  public function soonToExpireLicenseNoticeCondition( $args ) {
154
+ if (!current_user_can('manage_options')) {
155
+ return false;
156
+ }
157
+
158
  if ( ! isset( $args['addon'] ) ) return false;
159
  $manager = $this->getManager();
160
  if ( !($license = $manager->getLicense( $args['addon'] )) ) {
includes/Aventura/Wprss/Core/Model/AdminAjaxNotice/ServiceProvider.php CHANGED
@@ -207,7 +207,7 @@ class ServiceProvider extends AbstractComponentServiceProvider implements Servic
207
  'id' => 'settings_import_success',
208
  'notice_type' => NoticeInterface::TYPE_UPDATED,
209
  'condition' => $this->_getCommandIsWprssPage($c),
210
- 'content' => $this->_autoParagraph($this->__('All options are restored successfully.')),
211
  'dismiss_mode' => NoticeInterface::DISMISS_MODE_FRONTEND,
212
  ), $c);
213
 
207
  'id' => 'settings_import_success',
208
  'notice_type' => NoticeInterface::TYPE_UPDATED,
209
  'condition' => $this->_getCommandIsWprssPage($c),
210
+ 'content' => $this->_autoParagraph(__('Your settings were imported successfully', 'wprss')),
211
  'dismiss_mode' => NoticeInterface::DISMISS_MODE_FRONTEND,
212
  ), $c);
213
 
includes/admin-debugging.php CHANGED
@@ -3,25 +3,6 @@
3
  use Interop\Container\Exception\NotFoundException as ServiceNotFoundException;
4
  use Aventura\Wprss\Core\Model\AdminAjaxNotice\NoticeInterface;
5
  use Psr\Log\LogLevel;
6
-
7
- /**
8
- * Plugin debugging
9
- *
10
- * @package WPRSSAggregator
11
- * @subpackage Includes
12
- * @since 3.0
13
- * @author Jean Galea <info@jeangalea.com>
14
- * @copyright Copyright(c) 2012-2015, Jean Galea
15
- * @link http://www.wprssaggregator.com
16
- * @license http://www.gnu.org/licenses/gpl.html
17
- */
18
-
19
- /*
20
- //allow redirection, even if my theme starts to send output to the browser
21
- add_action( 'admin_init', 'wprss_do_output_buffer' );
22
- function wprss_do_output_buffer() {
23
- //ob_start();
24
- }*/
25
 
26
 
27
  /**
@@ -276,8 +257,6 @@ use Psr\Log\LogLevel;
276
 
277
  do_action( 'wprss_debugging_after' );
278
 
279
- wprss_system_info();
280
-
281
  if ( count($bottom) > 0 ) {
282
  foreach( $bottom as $id => $data ) {
283
  if ( !isset( $data['render'] ) ) continue;
@@ -288,7 +267,6 @@ use Psr\Log\LogLevel;
288
  ?>
289
  </div>
290
  <?php
291
- wp_enqueue_script('wpra-admin-debug-js', WPRSS_JS . 'admin-debug.js', ['jquery'], WPRSS_VERSION, true);
292
  }
293
 
294
 
3
  use Interop\Container\Exception\NotFoundException as ServiceNotFoundException;
4
  use Aventura\Wprss\Core\Model\AdminAjaxNotice\NoticeInterface;
5
  use Psr\Log\LogLevel;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
 
7
 
8
  /**
257
 
258
  do_action( 'wprss_debugging_after' );
259
 
 
 
260
  if ( count($bottom) > 0 ) {
261
  foreach( $bottom as $id => $data ) {
262
  if ( !isset( $data['render'] ) ) continue;
267
  ?>
268
  </div>
269
  <?php
 
270
  }
271
 
272
 
includes/admin-display.php CHANGED
@@ -513,7 +513,7 @@
513
  }
514
 
515
  // Schedule the event for 5 seconds from now
516
- $offset = floor(count(wp_get_ready_cron_jobs()) / 2);
517
  $success = wp_schedule_single_event( time() + $offset, 'wprss_fetch_single_feed_hook', $schedule_args );
518
  if (!$success) {
519
  throw new Exception(__('Failed to schedule cron', 'wprss'));
@@ -562,7 +562,7 @@
562
  }
563
 
564
  // Schedule a job that runs this function with the source id parameter
565
- $offset = floor(count(wp_get_ready_cron_jobs()) / 2);
566
  $success = wp_schedule_single_event( time() + $offset, 'wprss_delete_feed_items_from_source_hook', array( $id ) );
567
  if (!$success) {
568
  throw new Exception(__('Failed to schedule cron', 'wprss'));
513
  }
514
 
515
  // Schedule the event for 5 seconds from now
516
+ $offset = floor(count(wpra_get_ready_cron_jobs()) / 2);
517
  $success = wp_schedule_single_event( time() + $offset, 'wprss_fetch_single_feed_hook', $schedule_args );
518
  if (!$success) {
519
  throw new Exception(__('Failed to schedule cron', 'wprss'));
562
  }
563
 
564
  // Schedule a job that runs this function with the source id parameter
565
+ $offset = floor(count(wpra_get_ready_cron_jobs()) / 2);
566
  $success = wp_schedule_single_event( time() + $offset, 'wprss_delete_feed_items_from_source_hook', array( $id ) );
567
  if (!$success) {
568
  throw new Exception(__('Failed to schedule cron', 'wprss'));
includes/admin-help-metaboxes.php CHANGED
@@ -40,7 +40,7 @@
40
 
41
  ' . 'This option only applies when using the shortcode to output feed items.', WPRSS_TEXT_DOMAIN),
42
 
43
- 'wprss_import_source' => __('Tick this box to import the feed source name and URL for each item individually.
44
 
45
  ' . 'This option is useful when importing from aggregated RSS feeds that have items from different sources.
46
 
40
 
41
  ' . 'This option only applies when using the shortcode to output feed items.', WPRSS_TEXT_DOMAIN),
42
 
43
+ 'wprss_import_source' => __('Tick this box to get the site name and URL from the RSS feed, for each item individually.
44
 
45
  ' . 'This option is useful when importing from aggregated RSS feeds that have items from different sources.
46
 
includes/admin-help-settings.php CHANGED
@@ -55,6 +55,11 @@ function wprss_settings_add_tooltips() {
55
 
56
  '. 'Default: <em>Latest items first</em>',
57
  WPRSS_TEXT_DOMAIN),
 
 
 
 
 
58
  // Feed processing interval
59
  'cron-interval' => __('How frequently should the feed sources (that do not have their own update interval) check for updates and fetch items accordingly.
60
 
55
 
56
  '. 'Default: <em>Latest items first</em>',
57
  WPRSS_TEXT_DOMAIN),
58
+ // Schedule future items
59
+ 'schedule_future_items' => __('If ticked, items with future dates will be scheduled to be published later. Leave unticked to always publish items immediately.
60
+ <hr/>
61
+ Default: <em>Off</em>',
62
+ 'wprss'),
63
  // Feed processing interval
64
  'cron-interval' => __('How frequently should the feed sources (that do not have their own update interval) check for updates and fetch items accordingly.
65
 
includes/admin-import-export.php DELETED
@@ -1,245 +0,0 @@
1
- <?php
2
-
3
- use Dhii\Di\WritableContainerInterface;
4
- use Aventura\Wprss\Core\Model\BulkSourceImport\ServiceProvider;
5
-
6
- /**
7
- * Build the import/export settings page, used to import and export the plugin's settings
8
- * Based on http://wp.tutsplus.com/tutorials/creative-coding/creating-a-simple-backuprestore-settings-feature/
9
- *
10
- * @since 3.1
11
- */
12
-
13
- add_action( 'admin_init', 'wp_rss_aggregator_bulk_import' );
14
- /**
15
- * Checks for the submission of a bulk import.
16
- * If a bulk submission is made, creates the feed sources.
17
- *
18
- * @since 4.5
19
- */
20
- function wp_rss_aggregator_bulk_import() {
21
- // Check if recieving
22
- if ( !empty( $_POST['bulk-feeds'] ) ) {
23
- // Check nonce
24
- \check_admin_referer('wprss-bulk-import', 'wprss-bulk-import');
25
- // Get the site which we should post to
26
- $post_site = \is_multisite() ? \get_current_blog_id() : '';
27
- // Get the text
28
- $bulk_feeds = $_POST['bulk-feeds'];
29
-
30
- $importer = \wprss_wp_container()->get(\WPRSS_SERVICE_ID_PREFIX.'bulk_source_import');
31
- /* @var $importer Aventura\Wprss\Core\Component\BulkSourceImport */
32
- $results = $importer->import($bulk_feeds);
33
-
34
- \wprss()->getAdminAjaxNotices()->addNotice('bulk_feed_import');
35
- }
36
- }
37
-
38
-
39
- add_action( 'admin_init', 'wp_rss_aggregator_export', 1 );
40
-
41
- /**
42
- * Handles exporting of aggregator settings
43
- *
44
- * @since 3.1
45
- */
46
- function wp_rss_aggregator_export() {
47
- if (!isset($_POST['wpra-export'])) {
48
- return;
49
- }
50
-
51
- $adminurl = strtolower( admin_url() );
52
- $referer = strtolower( wp_get_referer() );
53
- $nonce = 'wprss-settings-export';
54
- $result = isset($_POST['_wpnonce'])
55
- ? wp_verify_nonce($_POST['_wpnonce'], 'wprss-settings-export')
56
- : false;
57
-
58
- if ($result === false) {
59
- return;
60
- }
61
-
62
- $blogname = str_replace( " ", "", get_option( 'blogname' ) );
63
- $date = date( "m-d-Y" );
64
- $json_name = $blogname . "-" . $date; // Naming the filename that will be generated.
65
-
66
- header( 'Content-Description: File Transfer' );
67
- header( "Content-Type: text/json; charset=" . get_option( 'blog_charset' ) );
68
- header( "Content-Disposition: attachment; filename=$json_name.json" );
69
- wp_rss_set_export_data();
70
- die();
71
- }
72
-
73
-
74
- /**
75
- * Gathers relevant options, encodes them in Json and echoes the file
76
- *
77
- * @since 3.1
78
- */
79
- function wp_rss_set_export_data() {
80
- $options = apply_filters(
81
- 'wprss_fields_export',
82
- array( 'wprss_settings_general' => get_option( 'wprss_settings_general' ) )
83
- );
84
- $json_file = json_encode( $options );
85
-
86
- foreach ( $options as $key => $value ) {
87
- $value = maybe_unserialize( $value );
88
- $need_options[ $key ] = $value;
89
- }
90
- $json_file = json_encode( $need_options ); // Encode data into json data
91
- echo $json_file;
92
- die();
93
- }
94
-
95
-
96
- /**
97
- * Notice for a successful export
98
- *
99
- * @since 3.1
100
- */
101
- function wp_rss_aggregator_export_notice() {
102
- ?><div class="updated"><?php echo wpautop( __( 'All options are exported successfully.', WPRSS_TEXT_DOMAIN ) ) ?></div><?php
103
-
104
- }
105
-
106
-
107
- /**
108
- * Notice for a successful import
109
- *
110
- * @since 3.1
111
- */
112
- function wp_rss_aggregator_import_notice1() {
113
- ?><div class="updated"><?php echo wpautop( __( 'All options are restored successfully.', WPRSS_TEXT_DOMAIN ) ) ?></div><?php
114
-
115
- }
116
-
117
-
118
- /**
119
- * Notice for an unsuccessful import
120
- *
121
- * @since 3.1
122
- */
123
- function wp_rss_aggregator_import_notice2() {
124
- ?><div class="error"><?php echo wpautop( __( 'Invalid file or file size too big.', WPRSS_TEXT_DOMAIN ) ) ?></div><?php
125
-
126
- }
127
-
128
-
129
- add_action( 'admin_init', 'wp_rss_aggregator_import' );
130
- /**
131
- * Handles the importing of settings
132
- *
133
- * @since 3.1
134
- */
135
- function wp_rss_aggregator_import(){
136
- global $pagenow;
137
- if( $pagenow == 'admin.php' ) {
138
- //Hope this plugin don't use admin.php for anything
139
- return;
140
- }
141
- elseif ( $pagenow == 'edit.php' ) {
142
- if (isset($_POST['wpra-import']) && isset($_FILES['wpra-import-file'])) {
143
- check_admin_referer( 'wprss-settings-import' );
144
-
145
- $import = $_FILES['wpra-import-file'];
146
-
147
- if ( $import['error'] > 0) {
148
- wp_die( __('Error during import. Please upload a file', 'wprss') );
149
- } else {
150
- $file_name = $import['name'];
151
- $file_name_parts = explode( ".", $file_name );
152
- $file_ext = strtolower( end( $file_name_parts ) );
153
- $file_size = $import['size'];
154
- if ( ( $file_ext == "json" ) && ( $file_size < 500000 ) ) {
155
- $encode_options = file_get_contents( $import['tmp_name'] );
156
- $options = json_decode( $encode_options, true );
157
- foreach ( $options as $key => $value ) {
158
- update_option( $key, $value );
159
- }
160
- wprss()->getAdminAjaxNotices()->addNotice('settings_import_success');
161
- do_action( 'wprss_settings_imported' );
162
- }
163
- else {
164
- wprss()->getAdminAjaxNotices()->addNotice('settings_import_failed');
165
- }
166
- }
167
- }
168
- }
169
- }
170
-
171
-
172
- /**
173
- * Handles the import/export page display
174
- *
175
- * @since 3.1
176
- */
177
- function wprss_import_export_settings_page_display() {
178
- if ( !isset( $_POST['export'] ) ) { ?>
179
- <div class="wrap">
180
- <!-- Bulk Add -->
181
- <h2><?php _e( 'Bulk Feed Import', WPRSS_TEXT_DOMAIN ); ?></h2>
182
- <p><?php _e( 'Import multiple feed sources at once, by entering the name and URLs of your feeds below.', WPRSS_TEXT_DOMAIN ); ?></p>
183
- <p><?php _e( 'Separate the name and the URL using a comma on each line:', WPRSS_TEXT_DOMAIN ); ?>
184
- <code><?php _e( 'Feed Name, http://www.myfeed.com', WPRSS_TEXT_DOMAIN ); ?></code>
185
- </p>
186
- <form id="bulk-add-form" method="POST">
187
- <textarea rows="6" cols="80" form="bulk-add-form" name="bulk-feeds" autofocus></textarea>
188
- <br/>
189
- <?php wp_nonce_field('wprss-bulk-import', 'wprss-bulk-import'); ?>
190
- <input type="submit" class="button-secondary" name="bulk-add" value="<?php _e( 'Bulk Import', WPRSS_TEXT_DOMAIN ) ?>" />
191
- </form>
192
- <hr/>
193
-
194
- <!-- Settings Import/Export -->
195
- <h2><?php _e( 'Import & Export Settings', WPRSS_TEXT_DOMAIN ); ?></h2>
196
-
197
- <h3><?php _e( 'Export Settings', WPRSS_TEXT_DOMAIN ); ?></h3>
198
- <?php echo wpautop( __( 'Click the <strong>Export Settings</strong> button to generate a file containing all the settings used by WP RSS Aggregator', WPRSS_TEXT_DOMAIN ) ) ?>
199
- <?php echo wpautop( __( 'After exporting, you can either use the backup file to restore your settings to this site or to another WordPress site.', WPRSS_TEXT_DOMAIN ) ) ?>
200
- <?php do_action( 'wprss_export_section' ); ?>
201
- <form method="post">
202
- <p class="submit">
203
- <?php wp_nonce_field( 'wprss-settings-export' ); ?>
204
- <input type="submit"
205
- name="wpra-export"
206
- value="<?php _e( 'Export Settings', WPRSS_TEXT_DOMAIN); ?>"
207
- class="button" />
208
- </p>
209
- </form>
210
-
211
- <h3><?php _e( 'Import Settings', WPRSS_TEXT_DOMAIN ); ?></h3>
212
- <?php echo wpautop( __( 'Click the <strong>Choose file</strong> button and choose a backup file.', WPRSS_TEXT_DOMAIN ) ) ?>
213
- <?php echo wpautop( __( 'Press the <strong>Import Settings</strong> button, and WordPress will do the rest for you.', WPRSS_TEXT_DOMAIN ) ) ?>
214
- <?php do_action( 'wprss_import_section' ); ?>
215
- <form method='post' enctype='multipart/form-data'>
216
- <p class="submit">
217
- <?php wp_nonce_field( 'wprss-settings-import' ); ?>
218
- <input type='file' name='wpra-import-file' />
219
- <input type='submit'
220
- name='wpra-import'
221
- value="<?php _e( 'Import Settings', WPRSS_TEXT_DOMAIN ); ?>"
222
- class="button" />
223
- </p>
224
- </form>
225
-
226
- <h3><?php _e( 'Importing/Exporting Feed Sources', WPRSS_TEXT_DOMAIN ); ?></h3>
227
- <?php echo wpautop( sprintf( __( 'To import/export your feed sources, please use the standard WordPress <a href="%1$simport.php">Import</a> and <a href="%1$sexport.php">Export</a> functionality.', WPRSS_TEXT_DOMAIN ), get_admin_url() ) ) ?>
228
- <?php echo wpautop( sprintf( __( 'On the <a href="%1$sexport.php">Export</a> page, check the <strong>Feed Sources</strong> radio button and click the <strong>Download Export File</strong> button. WordPress will then create an XML file containing all the feed sources.', WPRSS_TEXT_DOMAIN ), get_admin_url() ) ) ?>
229
- <?php echo wpautop( sprintf( __( 'On the <a href="%1$simport.php">Import</a> page, choose the previously created file and click the <strong>Upload file and import</strong> button.', WPRSS_TEXT_DOMAIN ), get_admin_url() ) ) ?>
230
-
231
- </div>
232
- <?php
233
- }
234
- }
235
-
236
-
237
- // Adds the bulk import service provider to the core container
238
- add_filter(\WPRSS_EVENT_PREFIX .'core_container_init', function(WritableContainerInterface $container) {
239
- $serviceProvider = new ServiceProvider(array(
240
- 'notice_service_id_prefix' => \WPRSS_NOTICE_SERVICE_ID_PREFIX,
241
- 'service_id_prefix' => \WPRSS_SERVICE_ID_PREFIX,
242
- 'event_prefix' => \WPRSS_EVENT_PREFIX,
243
- ));
244
- $container->register($serviceProvider);
245
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
includes/admin-metaboxes.php CHANGED
@@ -1,5 +1,22 @@
1
  <?php
2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  add_action( 'add_meta_boxes', 'wprss_add_meta_boxes', 99);
4
  /**
5
  * Set up the input boxes for the wprss_feed post type
@@ -9,21 +26,6 @@
9
  function wprss_add_meta_boxes() {
10
  global $wprss_meta_fields;
11
 
12
- // Remove the default WordPress Publish box, because we will be using custom ones
13
- remove_meta_box( 'submitdiv', 'wprss_feed', 'side' );
14
-
15
- // Remove some plugin's metaboxes because they're not relevant to the wprss_feed post type.
16
- wprss_remove_unrelated_meta_boxes();
17
-
18
- add_meta_box(
19
- 'submitdiv', // $id
20
- __( 'Save Feed Source', WPRSS_TEXT_DOMAIN ), // $title
21
- 'post_submit_meta_box', // $callback
22
- 'wprss_feed', // $page
23
- 'side', // $context
24
- 'high' // $priority
25
- );
26
-
27
  add_meta_box(
28
  'preview_meta_box',
29
  __( 'Feed Preview', WPRSS_TEXT_DOMAIN ),
@@ -120,7 +122,7 @@
120
  );
121
 
122
  $wprss_meta_fields[ 'import_source' ] = array(
123
- 'label' => __( 'Import source info', WPRSS_TEXT_DOMAIN ),
124
  'id' => $prefix . 'import_source',
125
  'type' => 'checkbox',
126
  );
1
  <?php
2
 
3
+ add_action( 'add_meta_boxes', function () {
4
+ // Remove some plugin's metaboxes because they're not relevant to the wprss_feed post type.
5
+ wprss_remove_unrelated_meta_boxes();
6
+
7
+ // Remove the default WordPress Publish box, because we will be using custom ones
8
+ remove_meta_box( 'submitdiv', 'wprss_feed', 'side' );
9
+ // Custom Publish box
10
+ add_meta_box(
11
+ 'submitdiv',
12
+ __( 'Save Feed Source', WPRSS_TEXT_DOMAIN ),
13
+ 'post_submit_meta_box',
14
+ 'wprss_feed',
15
+ 'side',
16
+ 'high'
17
+ );
18
+ });
19
+
20
  add_action( 'add_meta_boxes', 'wprss_add_meta_boxes', 99);
21
  /**
22
  * Set up the input boxes for the wprss_feed post type
26
  function wprss_add_meta_boxes() {
27
  global $wprss_meta_fields;
28
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
  add_meta_box(
30
  'preview_meta_box',
31
  __( 'Feed Preview', WPRSS_TEXT_DOMAIN ),
122
  );
123
 
124
  $wprss_meta_fields[ 'import_source' ] = array(
125
+ 'label' => __( 'Use source info', WPRSS_TEXT_DOMAIN ),
126
  'id' => $prefix . 'import_source',
127
  'type' => 'checkbox',
128
  );
includes/admin-options.php CHANGED
@@ -156,6 +156,10 @@
156
  'label' => __( 'Limit feed items per import', 'wprss' ),
157
  'callback' => 'wprss_setting_limit_feed_items_per_import_callback'
158
  ),
 
 
 
 
159
  ),
160
 
161
  'custom_feed' => array(
@@ -297,11 +301,6 @@
297
  }
298
  }
299
 
300
- // If user requested to download system info, generate the download.
301
- if ( isset( $_POST['wprss-sysinfo'] ) ) {
302
- do_action('wprss_download_sysinfo');
303
- }
304
-
305
  do_action( 'wprss_admin_init' );
306
  }
307
 
@@ -598,6 +597,22 @@
598
  <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
599
  }
600
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
601
  /**
602
  * Renders the `feed_items_import_order` setting.
603
  *
156
  'label' => __( 'Limit feed items per import', 'wprss' ),
157
  'callback' => 'wprss_setting_limit_feed_items_per_import_callback'
158
  ),
159
+ 'schedule_future_items' => array(
160
+ 'label' => __( 'Schedule future items', 'wprss' ),
161
+ 'callback' => 'wprss_setting_schedule_future_items_callback'
162
+ ),
163
  ),
164
 
165
  'custom_feed' => array(
301
  }
302
  }
303
 
 
 
 
 
 
304
  do_action( 'wprss_admin_init' );
305
  }
306
 
597
  <?php echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
598
  }
599
 
600
+ /**
601
+ * Renders the `limit_feed_items_per_import` setting.
602
+ *
603
+ * @since 4.17
604
+ *
605
+ * @param array $field Field data.
606
+ */
607
+ function wprss_setting_schedule_future_items_callback($field)
608
+ {
609
+ $id = $field['field_id'];
610
+ $value = wprss_get_general_setting($id);
611
+
612
+ echo wprss_options_render_checkbox( $field['field_id'], 'schedule_future_items', $value );
613
+ echo wprss_settings_inline_help( $field['field_id'], $field['tooltip'] );
614
+ }
615
+
616
  /**
617
  * Renders the `feed_items_import_order` setting.
618
  *
includes/admin.php CHANGED
@@ -24,19 +24,10 @@
24
  </style>
25
  <?php }
26
 
27
-
28
- add_action('admin_menu', function () {
29
- add_submenu_page( 'edit.php?post_type=wprss_feed', __( 'Export & Import Settings', WPRSS_TEXT_DOMAIN ), __( 'Import & Export', WPRSS_TEXT_DOMAIN ), apply_filters( 'wprss_capability', 'manage_feed_settings' ), 'wprss-import-export-settings', 'wprss_import_export_settings_page_display' );
30
- }, 20);
31
-
32
  add_action('admin_menu', function () {
33
  add_submenu_page( 'edit.php?post_type=wprss_feed', __( 'WP RSS Aggregator Settings', WPRSS_TEXT_DOMAIN ), __( 'Settings', WPRSS_TEXT_DOMAIN ), apply_filters( 'wprss_capability', 'manage_feed_settings' ), 'wprss-aggregator-settings', 'wprss_settings_page_display' );
34
  }, 30);
35
 
36
- add_action('admin_menu', function () {
37
- add_submenu_page( 'edit.php?post_type=wprss_feed', __( 'Debugging', WPRSS_TEXT_DOMAIN ), __( 'Debugging', WPRSS_TEXT_DOMAIN ), apply_filters( 'wprss_capability', 'manage_feed_settings'), 'wprss-debugging', 'wprss_debugging_page_display' );
38
- }, 40);
39
-
40
  add_action('admin_menu', function () {
41
  add_submenu_page( 'edit.php?post_type=wprss_feed', __( 'Help & Support', WPRSS_TEXT_DOMAIN ), __( 'Help & Support', WPRSS_TEXT_DOMAIN ), apply_filters( 'wprss_capability', 'manage_feed_settings'), 'wprss-help', 'wprss_help_page_display' );
42
  }, 60);
@@ -98,7 +89,7 @@
98
  */
99
  function wprss_change_title_text($original) {
100
  if (get_post_type() === 'wprss_feed') {
101
- return __('Name this feed (e.g. WP Mayor)', WPRSS_TEXT_DOMAIN);
102
  }
103
 
104
  return $original;
24
  </style>
25
  <?php }
26
 
 
 
 
 
 
27
  add_action('admin_menu', function () {
28
  add_submenu_page( 'edit.php?post_type=wprss_feed', __( 'WP RSS Aggregator Settings', WPRSS_TEXT_DOMAIN ), __( 'Settings', WPRSS_TEXT_DOMAIN ), apply_filters( 'wprss_capability', 'manage_feed_settings' ), 'wprss-aggregator-settings', 'wprss_settings_page_display' );
29
  }, 30);
30
 
 
 
 
 
31
  add_action('admin_menu', function () {
32
  add_submenu_page( 'edit.php?post_type=wprss_feed', __( 'Help & Support', WPRSS_TEXT_DOMAIN ), __( 'Help & Support', WPRSS_TEXT_DOMAIN ), apply_filters( 'wprss_capability', 'manage_feed_settings'), 'wprss-help', 'wprss_help_page_display' );
33
  }, 60);
89
  */
90
  function wprss_change_title_text($original) {
91
  if (get_post_type() === 'wprss_feed') {
92
+ return __('Name this feed', WPRSS_TEXT_DOMAIN);
93
  }
94
 
95
  return $original;
includes/cron-jobs.php CHANGED
@@ -1,222 +1,301 @@
1
  <?php
2
- /**
3
- * Contains all the cron jobs in use by WP RSS Aggregator
4
- *
5
- * @package WPRSSAggregator
6
- */
7
 
8
-
9
- // CRON HOOKS TO ACTIVATE/PAUSE FEED SOURCES
10
- add_action( 'wprss_activate_feed_schedule_hook', 'wprss_activate_feed_source', 10, 1 );
11
- add_action( 'wprss_pause_feed_schedule_hook', 'wprss_pause_feed_source', 10 , 1 );
12
-
13
-
14
- add_action( 'init', 'wprss_schedule_fetch_all_feeds_cron' );
15
- /**
16
- * Creates the cron to fetch feeds every hour
17
- *
18
- * @since 2.0
19
- */
20
- function wprss_schedule_fetch_all_feeds_cron() {
21
-
22
- $cron_interval = wprss_get_general_setting('cron_interval');
23
-
24
- // verify event has not been scheduled
25
- if ( ! wp_next_scheduled( 'wprss_fetch_all_feeds_hook' ) ) {
26
- // Schedule to run hourly
27
- wp_schedule_event( time(), $cron_interval, 'wprss_fetch_all_feeds_hook' );
28
- }
29
-
30
- add_action( 'wprss_fetch_all_feeds_hook', 'wprss_fetch_insert_all_feed_items_from_cron' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  }
32
 
33
-
34
- add_action( 'init', 'wprss_schedule_truncate_posts_cron' );
35
- /**
36
- * Creates the cron to truncate wprss_feed_item posts daily
37
- *
38
- * @since 2.0
39
- */
40
- function wprss_schedule_truncate_posts_cron() {
41
-
42
- // verify event has not been scheduled
43
- if ( ! wp_next_scheduled( 'wprss_truncate_posts_hook') ) {
44
- // Schedule to run daily
45
- wp_schedule_event( time(), 'daily', 'wprss_truncate_posts_hook' );
46
- }
47
-
48
- add_action( 'wprss_truncate_posts_hook', 'wprss_truncate_posts' );
49
  }
50
 
51
-
52
- // filter to add new possible frequencies to the cron
53
- add_filter( 'cron_schedules', 'wprss_filter_cron_schedules' );
54
- /**
55
- * Adding a few more handy cron schedules to the default ones
56
- * @since 3.0
57
- */
58
- function wprss_filter_cron_schedules( $schedules) {
59
- $frequencies = array(
60
- 'five_min' => array(
61
- 'interval' => 5 * MINUTE_IN_SECONDS,
62
- 'display' => __( 'Once every five minutes', WPRSS_TEXT_DOMAIN )
63
- ),
64
- 'ten_min' => array(
65
- 'interval' => 10 * MINUTE_IN_SECONDS,
66
- 'display' => __( 'Once every ten minutes', WPRSS_TEXT_DOMAIN )
67
- ),
68
- 'fifteen_min' => array(
69
- 'interval' => 15 * MINUTE_IN_SECONDS,
70
- 'display' => __( 'Once every fifteen minutes', WPRSS_TEXT_DOMAIN )
71
- ),
72
- 'thirty_min' => array(
73
- 'interval' => 30 * MINUTE_IN_SECONDS,
74
- 'display' => __( 'Once every thirty minutes', WPRSS_TEXT_DOMAIN )
75
- ),
76
- 'two_hours' => array(
77
- 'interval' => 2 * HOUR_IN_SECONDS,
78
- 'display' => __( 'Once every two hours', WPRSS_TEXT_DOMAIN )
79
- ),
80
- );
81
-
82
- return array_merge( $schedules, $frequencies );
83
  }
84
 
85
-
86
-
87
-
88
- /**
89
- * Deletes a custom cron schedule.
90
- *
91
- * Credits: WPCrontrol
92
- *
93
- * @param string $name The internal_name of the schedule to delete.
94
- * @since 3.7
95
- */
96
- function wprss_delete_schedule($name) {
97
- $scheds = get_option('crontrol_schedules',array());
98
- unset($scheds[$name]);
99
- update_option('crontrol_schedules', $scheds);
100
  }
101
-
102
-
103
-
104
-
105
- /**
106
- * Updates the feed processing cron job schedules.
107
- * Removes the current schedules and adds the ones in the feed source's meta.
108
- *
109
- * @param $feed_id The id of the wprss_feed
110
- * @since 3.8
111
- */
112
- function wprss_update_feed_processing_schedules( $feed_id ) {
113
- // Get the new feed processing schedules
114
- $activate = get_post_meta( $feed_id, 'wprss_activate_feed', TRUE );
115
- $pause = get_post_meta( $feed_id, 'wprss_pause_feed', TRUE );
116
-
117
- $schedule_args = array( $feed_id );
118
-
119
- if ( $activate !== '' ) {
120
- // Convert the meta data values to time stamps
121
- $new_activate_time = wprss_strtotime( $activate );
122
- // Get the current schedules
123
- $activate_feed_timestamp = wp_next_scheduled( 'wprss_activate_feed_schedule_hook', $schedule_args );
124
- // If a previous schedule exists, unschedule it
125
- if ( $activate_feed_timestamp !== FALSE ) {
126
- wp_unschedule_event( $activate_feed_timestamp, 'wprss_activate_feed_schedule_hook', $schedule_args );
127
- }
128
- wp_schedule_single_event( $new_activate_time, 'wprss_activate_feed_schedule_hook', $schedule_args );
129
- }
130
-
131
- if ( $pause !== '' ){
132
- // Convert the meta data values to time stamps
133
- $new_pause_time = wprss_strtotime( $pause );
134
- // Get the current schedules
135
- $pause_feed_timestamp = wp_next_scheduled( 'wprss_pause_feed_schedule_hook', $schedule_args );
136
- // If a previous schedule exists, unschedule it
137
- if ( $pause_feed_timestamp !== FALSE ) {
138
- wp_unschedule_event( $pause_feed_timestamp, 'wprss_pause_feed_schedule_hook', $schedule_args );
139
- }
140
- wp_schedule_single_event( $new_pause_time, 'wprss_pause_feed_schedule_hook', $schedule_args );
141
- }
142
-
143
  }
144
 
145
-
146
- add_action( 'wprss_on_feed_source_activated', 'wprss_feed_source_update_start_schedule' );
147
- /**
148
- * Starts the looping schedule for a feed source. Runs on a schedule
149
- *
150
- * @param $feed_id The ID of the feed source
151
- * @since 3.9
152
- */
153
- function wprss_feed_source_update_start_schedule( $feed_id ) {
154
- // Stop any currently scheduled update operations
155
- wprss_feed_source_update_stop_schedule( $feed_id );
156
- // Prepare the schedule
157
- $schedule_args = array( strval( $feed_id ) );
158
-
159
- // Get the interval
160
- $interval = get_post_meta( $feed_id, 'wprss_update_interval', TRUE );
161
- // Do nothing if the feed source has no update interval (not sure if possible) or if the interval
162
- // is set to global
163
- if ( $interval === '' || $interval === wprss_get_default_feed_source_update_interval() ) return;
164
-
165
- wp_schedule_event( time(), $interval , 'wprss_fetch_single_feed_hook', $schedule_args );
166
  }
167
-
168
-
169
- add_action( 'wprss_on_feed_source_paused', 'wprss_feed_source_update_stop_schedule' );
170
- /**
171
- * Stops any scheduled update operations for a feed source. Runs on a schedule.
172
- *
173
- * @param $feed_id The ID of the feed source ( wprss_feed )
174
- * @since 3.9
175
- */
176
- function wprss_feed_source_update_stop_schedule( $feed_id ) {
177
- $schedule_timestamp = wprss_get_next_feed_source_update( $feed_id );
178
- // If a schedule exists, unschedule it
179
- if ( $schedule_timestamp !== FALSE ) {
180
- $schedule_args = array( strval( $feed_id ) );
181
- wp_unschedule_event( $schedule_timestamp, 'wprss_fetch_single_feed_hook', $schedule_args );
182
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
183
  }
184
 
185
-
186
- /**
187
- * Returns the timestamp for the next feed source update
188
- *
189
- * @param $feed_id The ID of the feed source ( wprss_feed )
190
- * @return The timestamp of the next update operation, or false is no
191
- * update is scheduled.
192
- * @since 3.9
193
- */
194
- function wprss_get_next_feed_source_update( $feed_id ) {
195
- $schedule_args = array( strval( $feed_id ) );
196
- $timestamp = wp_next_scheduled( 'wprss_fetch_single_feed_hook', $schedule_args );
197
- return $timestamp;
198
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
199
 
200
-
201
- /**
202
- * Parses the date time string into a UTC timestamp.
203
- * The string must be in the format: m/d/y h:m:s
204
- *
205
- * @since 3.9
206
- */
207
- function wprss_strtotime( $str ){
208
- $parts = explode(' ', $str);
209
- $date = explode( '/', $parts[0] );
210
- $time = explode( ':', $parts[1] );
211
- return mktime( $time[0], $time[1], $time[2], $date[1], $date[0], $date[2] );
212
  }
213
 
214
-
215
- /**
216
- * Returns the default value for the per feed source update interval
217
- *
218
- * @since 3.9
219
- */
220
- function wprss_get_default_feed_source_update_interval() {
221
- return 'global';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
222
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  <?php
 
 
 
 
 
2
 
3
+ define('WPRA_FETCH_ALL_FEEDS_HOOK', 'wprss_fetch_all_feeds_hook');
4
+ define('WPRA_FETCH_FEED_HOOK', 'wprss_fetch_single_feed_hook');
5
+ define('WPRA_TRUNCATE_ITEMS_HOOK', 'wprss_truncate_posts_hook');
6
+ define('WPRA_ACTIVATE_FEED_HOOK', 'wprss_activate_feed_schedule_hook');
7
+ define('WPRA_PAUSE_FEED_HOOK', 'wprss_pause_feed_schedule_hook');
8
+
9
+ define('WPRA_TRUNCATE_ITEMS_INTERVAL', 'daily');
10
+
11
+ /**
12
+ * Alias for add_action, primarily used for readability to distinguish between cron-events and normal hooks.
13
+ *
14
+ * @since 4.17
15
+ *
16
+ * @param string $cron The cron hook event.
17
+ * @param callable $callback The callback to invoke for the cron.
18
+ */
19
+ function wpra_on_cron_do($cron, $callback)
20
+ {
21
+ add_action($cron, $callback);
22
+ }
23
+
24
+ // Cron events
25
+ wpra_on_cron_do(WPRA_FETCH_ALL_FEEDS_HOOK, 'wprss_fetch_insert_all_feed_items_from_cron');
26
+ wpra_on_cron_do(WPRA_TRUNCATE_ITEMS_HOOK, 'wprss_truncate_posts');
27
+ wpra_on_cron_do(WPRA_ACTIVATE_FEED_HOOK, 'wprss_activate_feed_source', 10, 1);
28
+ wpra_on_cron_do(WPRA_PAUSE_FEED_HOOK, 'wprss_pause_feed_source', 10, 1);
29
+
30
+ // Initialize crons that must always be scheduled
31
+ add_action('init', 'wpra_init_crons');
32
+
33
+ // When a feed source is activated, schedule its fetch cron
34
+ add_action('wprss_on_feed_source_activated', 'wprss_feed_source_update_start_schedule');
35
+
36
+ // When a feed source is paused, cancel its fetch cron
37
+ add_action('wprss_on_feed_source_paused', 'wprss_feed_source_update_stop_schedule');
38
+
39
+ // Filter the possible cron intervals to add more options
40
+ add_filter('cron_schedules', 'wprss_filter_cron_schedules');
41
+
42
+ /**
43
+ * Initializes the cron jobs.
44
+ *
45
+ * @since 4.17
46
+ */
47
+ function wpra_init_crons()
48
+ {
49
+ wprss_schedule_fetch_all_feeds_cron();
50
+ wprss_schedule_truncate_posts_cron();
51
+ }
52
+
53
+ /**
54
+ * Creates the cron to fetch feeds.
55
+ *
56
+ * @since 2.0
57
+ */
58
+ function wprss_schedule_fetch_all_feeds_cron()
59
+ {
60
+ // Check if the global fetch is scheduled
61
+ if (wp_next_scheduled(WPRA_FETCH_ALL_FEEDS_HOOK)) {
62
+ return;
63
  }
64
 
65
+ // If the event is not scheduled, schedule it
66
+ $interval = wprss_get_general_setting('cron_interval');
67
+ wp_schedule_event(time(), $interval, WPRA_FETCH_ALL_FEEDS_HOOK);
68
+ }
69
+
70
+ /**
71
+ * Creates the cron to truncate wprss_feed_item posts daily
72
+ *
73
+ * @since 2.0
74
+ */
75
+ function wprss_schedule_truncate_posts_cron()
76
+ {
77
+ // Check if the truncatation cron is scheduled
78
+ if (wp_next_scheduled(WPRA_TRUNCATE_ITEMS_HOOK)) {
79
+ return;
 
80
  }
81
 
82
+ // If not, schedule it
83
+ wp_schedule_event(time(), WPRA_TRUNCATE_ITEMS_INTERVAL, WPRA_TRUNCATE_ITEMS_HOOK);
84
+ }
85
+
86
+ /**
87
+ * Updates the feed processing cron job schedules.
88
+ * Removes the current schedules and adds the ones in the feed source's meta.
89
+ *
90
+ * @since 3.8
91
+ *
92
+ * @param int $feed_id The id of the wprss_feed
93
+ */
94
+ function wprss_update_feed_processing_schedules($feed_id)
95
+ {
96
+ // Get the feed's activate and pause times
97
+ $activate = get_post_meta($feed_id, 'wprss_activate_feed', true);
98
+ $pause = get_post_meta($feed_id, 'wprss_pause_feed', true);
99
+
100
+ // Parse as time strings
101
+ $activate = wprss_strtotime($activate);
102
+ $pause = wprss_strtotime($pause);
103
+
104
+ if (!empty($activate)) {
105
+ wpra_reschedule($activate, WPRA_ACTIVATE_FEED_HOOK, null, [$feed_id]);
 
 
 
 
 
 
 
 
106
  }
107
 
108
+ if ($pause !== '') {
109
+ wpra_reschedule($pause, WPRA_PAUSE_FEED_HOOK, null, [$feed_id]);
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  }
111
+ }
112
+
113
+ /**
114
+ * Starts the looping schedule for a feed source. Runs on a schedule
115
+ *
116
+ * @since 3.9
117
+ *
118
+ * @param int $feed_id The ID of the feed source
119
+ */
120
+ function wprss_feed_source_update_start_schedule($feed_id)
121
+ {
122
+ // Stop any currently scheduled update operations
123
+ wprss_feed_source_update_stop_schedule($feed_id);
124
+
125
+ // Get the interval
126
+ $interval = get_post_meta($feed_id, 'wprss_update_interval', true);
127
+ // Do nothing if the feed source has no update interval (not sure if possible) or if the interval
128
+ // is set to global
129
+ if ($interval === '' || $interval === wprss_get_default_feed_source_update_interval()) {
130
+ return;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
131
  }
132
 
133
+ wp_schedule_event(time(), $interval, WPRA_FETCH_FEED_HOOK, [strval($feed_id)]);
134
+ }
135
+
136
+ /**
137
+ * Stops any scheduled update operations for a feed source. Runs on a schedule.
138
+ *
139
+ * @since 3.9
140
+ *
141
+ * @param int $feed_id The ID of the feed source ( wprss_feed )
142
+ */
143
+ function wprss_feed_source_update_stop_schedule($feed_id)
144
+ {
145
+ $timestamp = wprss_get_next_feed_source_update($feed_id);
146
+
147
+ // If a schedule exists, unschedule it
148
+ if ($timestamp !== false) {
149
+ wp_unschedule_event($timestamp, WPRA_FETCH_FEED_HOOK, [strval($feed_id)]);
 
 
 
 
150
  }
151
+ }
152
+
153
+ /**
154
+ * Returns the timestamp for the next feed source update
155
+ *
156
+ * @since 3.9
157
+ *
158
+ * @param int $feed_id The ID of the feed source ( wprss_feed )
159
+ *
160
+ * @return int The timestamp of the next update operation, or false is no
161
+ * update is scheduled.
162
+ */
163
+ function wprss_get_next_feed_source_update($feed_id)
164
+ {
165
+ return wp_next_scheduled(WPRA_FETCH_FEED_HOOK, [strval($feed_id)]);
166
+ }
167
+
168
+ /**
169
+ * Reschedules a cron event, unscheduling any existing matching crons.
170
+ *
171
+ * @since 4.17
172
+ *
173
+ * @param int $timestamp The timestamp.
174
+ * @param string $event The hook event.
175
+ * @param string|null $recurrence The recurrence.
176
+ * @param array $args Additional args.
177
+ */
178
+ function wpra_reschedule($timestamp, $event, $recurrence = null, $args = [])
179
+ {
180
+ $existing = wp_next_scheduled($event, $args);
181
+
182
+ if ($existing !== false) {
183
+ wp_unschedule_event($existing, $event, $args);
184
  }
185
 
186
+ if ($recurrence === null) {
187
+ wp_schedule_single_event($timestamp, $event, $args);
188
+ } else {
189
+ wp_schedule_event($timestamp, $recurrence, $event, $args);
 
 
 
 
 
 
 
 
 
190
  }
191
+ }
192
+
193
+ /**
194
+ * Retrieves the cron schedules that WPRA uses.
195
+ *
196
+ * @since 4.17
197
+ *
198
+ * @return array
199
+ */
200
+ function wpra_get_cron_schedules()
201
+ {
202
+ return [
203
+ 'five_min' => array(
204
+ 'display' => __('Once every 5 minutes', 'wprss'),
205
+ 'interval' => MINUTE_IN_SECONDS * 5,
206
+ ),
207
+ 'ten_min' => array(
208
+ 'display' => __('Once every 10 minutes', 'wprss'),
209
+ 'interval' => MINUTE_IN_SECONDS * 10,
210
+ ),
211
+ 'fifteen_min' => array(
212
+ 'display' => __('Once every 15 minutes', 'wprss'),
213
+ 'interval' => MINUTE_IN_SECONDS * 15,
214
+ ),
215
+ 'thirty_min' => array(
216
+ 'display' => __('Once every 30 minutes', 'wprss'),
217
+ 'interval' => MINUTE_IN_SECONDS * 30,
218
+ ),
219
+ 'two_hours' => array(
220
+ 'display' => __('Once every 2 hours', 'wprss'),
221
+ 'interval' => HOUR_IN_SECONDS * 2,
222
+ ),
223
+ 'weekly' => array(
224
+ 'display' => __('Once weekly', 'wprss'),
225
+ 'interval' => WEEK_IN_SECONDS,
226
+ ),
227
+ ];
228
+ }
229
+
230
+ /**
231
+ * Registers the cron schedules to WordPress, avoiding duplicates.
232
+ *
233
+ * @since 3.0
234
+ */
235
+ function wprss_filter_cron_schedules($schedules)
236
+ {
237
+ // Pluck out the intervals
238
+ $intervals = array_map(function ($schedule) {
239
+ return $schedule['interval'];
240
+ }, $schedules);
241
+ // Get a map of intervals -> keys for fast interval lookup
242
+ $intervalsMap = array_flip($intervals);
243
+
244
+ // Register each WPRA schedule
245
+ $wpraSchedules = wpra_get_cron_schedules();
246
+ foreach ($wpraSchedules as $key => $schedule) {
247
+ // If the interval already exists, skip the schedule
248
+ if (array_key_exists($schedule['interval'], $intervalsMap)) {
249
+ continue;
250
+ }
251
 
252
+ $schedules[$key] = $schedule;
 
 
 
 
 
 
 
 
 
 
 
253
  }
254
 
255
+ return $schedules;
256
+ }
257
+
258
+ /**
259
+ * Deletes a custom cron schedule.
260
+ *
261
+ * Credits: WPCrontrol
262
+ *
263
+ * @since 3.7
264
+ *
265
+ * @param string $name The internal_name of the schedule to delete.
266
+ */
267
+ function wprss_delete_schedule($name)
268
+ {
269
+ $scheds = get_option('crontrol_schedules', array());
270
+ unset($scheds[$name]);
271
+ update_option('crontrol_schedules', $scheds);
272
+ }
273
+
274
+ /**
275
+ * Parses the date time string into a UTC timestamp.
276
+ * The string must be in the format: m/d/y h:m:s
277
+ *
278
+ * @since 3.9
279
+ */
280
+ function wprss_strtotime($str)
281
+ {
282
+ if (empty($str)) {
283
+ return 0;
284
  }
285
+
286
+ $parts = explode(' ', $str);
287
+ $date = explode('/', $parts[0]);
288
+ $time = explode(':', $parts[1]);
289
+
290
+ return mktime($time[0], $time[1], $time[2], $date[1], $date[0], $date[2]);
291
+ }
292
+
293
+ /**
294
+ * Returns the default value for the per feed source update interval
295
+ *
296
+ * @since 3.9
297
+ */
298
+ function wprss_get_default_feed_source_update_interval()
299
+ {
300
+ return 'global';
301
+ }
includes/di.php CHANGED
@@ -3,13 +3,13 @@
3
  * The DI module
4
  */
5
 
 
 
 
6
  use Dhii\Di\CompositeContainer;
7
  use Dhii\Di\CompositeContainerInterface;
8
  use Dhii\Di\WritableCompositeContainerInterface;
9
- use Aventura\Wprss\Core\CompositeContainer as WpraCompositeContainer;
10
- use Aventura\Wprss\Core\ServiceProvider;
11
- use Aventura\Wprss\Core\Container;
12
- use Dhii\Di\FactoryInterface;
13
 
14
  define('WPRSS_SERVICE_ID_PREFIX', \WPRSS_PLUGIN_CODE . '.');
15
 
@@ -129,3 +129,13 @@ add_action('wprss_container_init', function(WritableCompositeContainerInterface
129
 
130
  $parent->add($container);
131
  });
 
 
 
 
 
 
 
 
 
 
3
  * The DI module
4
  */
5
 
6
+ use Aventura\Wprss\Core\CompositeContainer as WpraCompositeContainer;
7
+ use Aventura\Wprss\Core\Container;
8
+ use Aventura\Wprss\Core\ServiceProvider;
9
  use Dhii\Di\CompositeContainer;
10
  use Dhii\Di\CompositeContainerInterface;
11
  use Dhii\Di\WritableCompositeContainerInterface;
12
+ use Dhii\Di\WritableContainerInterface;
 
 
 
13
 
14
  define('WPRSS_SERVICE_ID_PREFIX', \WPRSS_PLUGIN_CODE . '.');
15
 
129
 
130
  $parent->add($container);
131
  });
132
+
133
+ // Adds the bulk import service provider to the old Aventura container
134
+ add_filter('wprss_core_container_init', function (WritableContainerInterface $container) {
135
+ $serviceProvider = new \Aventura\Wprss\Core\Model\BulkSourceImport\ServiceProvider(array(
136
+ 'notice_service_id_prefix' => \WPRSS_NOTICE_SERVICE_ID_PREFIX,
137
+ 'service_id_prefix' => \WPRSS_SERVICE_ID_PREFIX,
138
+ 'event_prefix' => \WPRSS_EVENT_PREFIX,
139
+ ));
140
+ $container->register($serviceProvider);
141
+ });
includes/fallback-mbstring.php CHANGED
@@ -199,7 +199,7 @@ class WPRSS_MBString {
199
  * @param $text The string to run the operation on.
200
  * @return string The string in lowercase.
201
  */
202
- public function mb_strtolower( $text ) {
203
  if ( function_exists( 'mb_strtolower' ) )
204
  return mb_strtolower( $text );
205
 
@@ -219,8 +219,8 @@ class WPRSS_MBString {
219
  * @return array The Latin-1 version of the array of matches.
220
  * @see mb_strtolower()
221
  */
222
- public function _unicode_caseflip( $matches ) {
223
  return $matches[0][0] . chr( ord( $matches[0][1] ) ^ 32 );
224
  }
225
 
226
- }
199
  * @param $text The string to run the operation on.
200
  * @return string The string in lowercase.
201
  */
202
+ public static function mb_strtolower( $text ) {
203
  if ( function_exists( 'mb_strtolower' ) )
204
  return mb_strtolower( $text );
205
 
219
  * @return array The Latin-1 version of the array of matches.
220
  * @see mb_strtolower()
221
  */
222
+ public static function _unicode_caseflip( $matches ) {
223
  return $matches[0][0] . chr( ord( $matches[0][1] ) ^ 32 );
224
  }
225
 
226
+ }
includes/feed-access.php CHANGED
@@ -442,7 +442,7 @@ class WPRSS_SimplePie_File extends SimplePie_File {
442
  $parser = new SimplePie_HTTP_Parser( $this->headers );
443
  if ( $parser->parse() ) {
444
  $this->headers = $parser->headers;
445
- $this->body = $parser->body;
446
  $this->status_code = $parser->status_code;
447
  if ( (in_array( $this->status_code, array( 300, 301, 302, 303, 307 ) ) || $this->status_code > 307 && $this->status_code < 400) && isset( $this->headers['location'] ) && $this->redirects < $redirects ) {
448
  $this->redirects++;
@@ -506,7 +506,7 @@ class WPRSS_SimplePie_File extends SimplePie_File {
506
  $parser = new SimplePie_HTTP_Parser( $this->headers );
507
  if ( $parser->parse() ) {
508
  $this->headers = $parser->headers;
509
- $this->body = $parser->body;
510
  $this->status_code = $parser->status_code;
511
  if ( (in_array( $this->status_code, array( 300, 301, 302, 303, 307 ) ) || $this->status_code > 307 && $this->status_code < 400) && isset( $this->headers['location'] ) && $this->redirects < $redirects ) {
512
  $this->redirects++;
@@ -523,7 +523,7 @@ class WPRSS_SimplePie_File extends SimplePie_File {
523
  $this->error = 'Unable to decode HTTP "gzip" stream';
524
  $this->success = false;
525
  } else {
526
- $this->body = $decoder->data;
527
  }
528
  break;
529
 
@@ -538,6 +538,7 @@ class WPRSS_SimplePie_File extends SimplePie_File {
538
  $this->error = 'Unable to decode HTTP "deflate" stream';
539
  $this->success = false;
540
  }
 
541
  break;
542
 
543
  default:
@@ -555,13 +556,26 @@ class WPRSS_SimplePie_File extends SimplePie_File {
555
  }
556
  } else {
557
  $this->method = SIMPLEPIE_FILE_SOURCE_LOCAL | SIMPLEPIE_FILE_SOURCE_FILE_GET_CONTENTS;
558
- if ( !$this->body = file_get_contents( $url ) ) {
 
559
  $this->error = 'file_get_contents could not read the file';
560
  $this->success = false;
561
  }
562
  }
563
  }
564
 
 
 
 
 
 
 
 
 
 
 
 
 
565
 
566
  /**
567
  * Additional preparation of the curl request.
442
  $parser = new SimplePie_HTTP_Parser( $this->headers );
443
  if ( $parser->parse() ) {
444
  $this->headers = $parser->headers;
445
+ $this->body = $this->_processBody($parser->body);
446
  $this->status_code = $parser->status_code;
447
  if ( (in_array( $this->status_code, array( 300, 301, 302, 303, 307 ) ) || $this->status_code > 307 && $this->status_code < 400) && isset( $this->headers['location'] ) && $this->redirects < $redirects ) {
448
  $this->redirects++;
506
  $parser = new SimplePie_HTTP_Parser( $this->headers );
507
  if ( $parser->parse() ) {
508
  $this->headers = $parser->headers;
509
+ $this->body = $this->_processBody($parser->body);
510
  $this->status_code = $parser->status_code;
511
  if ( (in_array( $this->status_code, array( 300, 301, 302, 303, 307 ) ) || $this->status_code > 307 && $this->status_code < 400) && isset( $this->headers['location'] ) && $this->redirects < $redirects ) {
512
  $this->redirects++;
523
  $this->error = 'Unable to decode HTTP "gzip" stream';
524
  $this->success = false;
525
  } else {
526
+ $this->body = $this->_processBody($decoder->data);
527
  }
528
  break;
529
 
538
  $this->error = 'Unable to decode HTTP "deflate" stream';
539
  $this->success = false;
540
  }
541
+ $this->body = $this->_processBody($this->body);
542
  break;
543
 
544
  default:
556
  }
557
  } else {
558
  $this->method = SIMPLEPIE_FILE_SOURCE_LOCAL | SIMPLEPIE_FILE_SOURCE_FILE_GET_CONTENTS;
559
+ $this->body = $this->_processBody(file_get_contents($url));
560
+ if ( !$this->body ) {
561
  $this->error = 'file_get_contents could not read the file';
562
  $this->success = false;
563
  }
564
  }
565
  }
566
 
567
+ /**
568
+ * Processes the raw response body for an RSS feed.
569
+ *
570
+ * @since 4.17
571
+ *
572
+ * @param string $body The raw HTTP response body string.
573
+ *
574
+ * @return string The processed response body string.
575
+ */
576
+ protected function _processBody($body) {
577
+ return trim($body);
578
+ }
579
 
580
  /**
581
  * Additional preparation of the curl request.
includes/feed-blacklist.php CHANGED
@@ -1,125 +1,78 @@
1
  <?php
2
 
3
- /**
4
- * This file contains all functions relating to the blacklisting of
5
- * imported feeds items.
6
- *
7
- * Blacklisting a feed item is in essence nothing more than a saved list
8
- * of feed items. When a feed item is imported, its normalized permalink
9
- * is tested against this list, and if found, the feed item is not
10
- * imported. Admins can add items to the blacklist, to prevent them
11
- * from being imported again.
12
- *
13
- * @package WP RSS Aggregator
14
- * @since 4.4
15
- */
16
-
17
  // Check if the 'blacklist' GET param is set
18
- add_action( 'admin_init', 'wprss_check_if_blacklist_item' );
19
  // Checks if the transient is set to show the notice
20
- add_action( 'admin_init', 'wprss_check_notice_transient' );
21
  // Register custom post type
22
  // Add the row actions to the targetted post type
23
- add_filter( 'post_row_actions', 'wprss_blacklist_row_actions', 10, 1 );
24
- // Check if deleting a blacklist item, from the GET parameter
25
- add_action( 'admin_init', 'wprss_check_if_blacklist_delete' );
26
- // Changes the wprss_blacklist table columns
27
- add_filter( 'manage_wprss_blacklist_posts_columns', 'wprss_blacklist_columns');
28
- // Prints the table data for each blacklist entry
29
- add_action( 'manage_wprss_blacklist_posts_custom_column' , 'wprss_blacklist_table_contents', 10, 2 );
30
- // Changes the wprss_blacklist bulk actions
31
- add_filter('bulk_actions-edit-wprss_blacklist','wprss_blacklist_bulk_actions', 5, 1 );
32
- // Adds the metabox to the blacklist new/edit page
33
- add_action('add_meta_boxes_wprss_blacklist', 'wprss_blacklist_add_meta_boxes');
34
-
35
- // Disables auto saving
36
- add_action( 'admin_enqueue_scripts', function () {
37
- if (get_post_type() === 'wprss_blacklist') {
38
- wp_dequeue_script('autosave');
39
- }
40
- });
41
-
42
- /**
43
- * Retrieves the blacklisted items.
44
- *
45
- * @since 4.4
46
- * @return array An associative array of blacklisted item, each entry
47
- * having the key as the permalink, and the value as the title.
48
- */
49
- function wprss_get_blacklist() {
50
- // Get the option
51
- $blacklist_option = get_option('wprss_blacklist');
52
- // If the option does not exist
53
- if ( $blacklist_option === FALSE || !is_array( $blacklist_option ) ) {
54
- // create it
55
- update_option( 'wprss_blacklist', array() );
56
- $blacklist_option = array();
57
- }
58
- return $blacklist_option;
59
- }
60
 
61
  /**
62
  * Creates a blacklist entry for the given feed item.
63
  *
64
  * @since 4.4
 
65
  * @param int|string The ID of the feed item to add to the blacklist
66
  */
67
- function wprss_blacklist_item( $ID ) {
68
- // Return if feed item is null
69
- if ( is_null( $ID ) ) return;
70
-
71
- // Get the feed item data
72
- $item_title = get_the_title( $ID );
73
- $item_permalink = get_post_meta( $ID, 'wprss_item_permalink', TRUE );
74
- // If not an imported item, stop
75
- if ( $item_permalink === '' ) {
76
- wpra_get_logger()->warning('Feed item with ID {0} was not blacklisted because its URL was empty', [$ID]);
77
- return;
78
- }
79
- // Prepare the data for blacklisting
80
- $title = apply_filters( 'wprss_blacklist_title', trim( $item_title ) );
81
- $permalink = apply_filters( 'wprss_blacklist_permalink', trim( $item_permalink ) );
82
-
83
- // Get the blacklisted items
84
- $blacklist = wprss_get_blacklist();
85
- // Add the item to the blacklist
86
- $blacklist[ $permalink ] = $title;
87
 
88
- // Delete the item
89
- wp_delete_post( $ID, TRUE );
 
 
 
 
90
 
91
- // Add the blacklisted item
92
- $id = wp_insert_post(array(
93
- 'post_title' => $title,
94
- 'post_type' => 'wprss_blacklist',
95
- 'post_status' => 'publish',
96
- 'meta_input' => [
97
- 'wprss_permalink' => $permalink,
98
- ],
99
- ));
 
 
 
 
 
 
 
 
 
100
  }
101
 
102
  /**
103
  * Determines whether the given item is blacklist.
104
  *
105
  * @since 4.4
 
106
  * @param string $permalink The permalink to look for in the saved option
 
107
  * @return bool TRUE if the permalink is found, FALSE otherwise.
108
  */
109
- function wprss_is_blacklisted( $permalink ) {
 
110
  if (empty($permalink)) {
111
  return false;
112
  }
113
 
114
- // Query the blacklist entries, for an item with the given permalink
115
- $query = new WP_Query(array(
116
- 'post_type' => 'wprss_blacklist',
117
- 'meta_key' => 'wprss_permalink',
118
- 'meta_value' => $permalink
119
- ));
120
 
121
- // Return TRUE if the query returned a result, FALSE otherwise
122
- return $query->have_posts();
123
  }
124
 
125
  /**
@@ -127,50 +80,55 @@ function wprss_is_blacklisted( $permalink ) {
127
  *
128
  * @since 4.4
129
  */
130
- function wprss_check_if_blacklist_item() {
131
- // If the GET param is not set, do nothing. Return.
132
- if ( empty( $_GET['wprss_blacklist'] ) ) return;
 
 
 
133
 
134
- // Get the ID from the GET param
135
- $ID = $_GET['wprss_blacklist'];
136
- // If the post does not exist, stop. Show a message
137
- if ( get_post($ID) === NULL ) {
138
- wp_die( __( 'The item you are trying to blacklist does not exist', WPRSS_TEXT_DOMAIN ) );
139
- }
140
 
141
- // If the post type is not correct,
142
- if ( get_post_meta( $ID, 'wprss_item_permalink', TRUE ) === '' || get_post_status( $ID ) !== 'trash' ) {
143
- wp_die( __( 'The item you are trying to blacklist is not valid!', WPRSS_TEXT_DOMAIN ) );
144
- }
145
 
146
- check_admin_referer( 'blacklist-item-' . $ID, 'wprss_blacklist_item' );
147
- wprss_blacklist_item( $ID );
148
 
149
- // Get the current post type for the current page
150
- $post_type = isset( $_GET['post_type'] )? $_GET['post_type'] : 'post';
151
- // Check the current page, and generate the URL query string for the page
152
- $paged = isset( $_GET['paged'] )? '&paged=' . $_GET['paged'] : '';
153
- // Set the notice transient
154
- set_transient( 'wprss_item_blacklist_notice', 'true' );
155
- // Refresh the page without the GET parameter
156
- wp_redirect( admin_url( "edit.php?post_type=$post_type&post_status=trash" . $paged ) );
157
- exit();
 
158
  }
159
 
160
  /**
161
  * Checks if the transient for the blacklist notice is set, and shows the notice
162
  * if it is set.
163
  */
164
- function wprss_check_notice_transient() {
165
- // Check if the transient exists
166
- $transient = get_transient( 'wprss_item_blacklist_notice' );
167
- if ( $transient !== FALSE ) {
168
- // Remove the transient
169
- delete_transient( 'wprss_item_blacklist_notice' );
170
- // Show the notice
171
- // add_action( 'admin_notices', 'wprss_blacklist_item_notice' );
 
172
  wprss()->getAdminAjaxNotices()->addNotice('blacklist_item_success');
173
- }
174
  }
175
 
176
  /**
@@ -178,162 +136,57 @@ function wprss_check_notice_transient() {
178
  * Default post type = wprss_feed_item
179
  *
180
  * @since 4.4
 
181
  * @param array $actions The row actions to be filtered
182
- * @return array The new filtered row actions
183
- */
184
- function wprss_blacklist_row_actions( $actions ) {
185
- // Check the current page, and generate the URL query string for the page
186
- $paged = isset( $_GET['paged'] )? '&paged=' . $_GET['paged'] : '';
187
-
188
-
189
- // Check the post type
190
- if ( get_post_status() == 'trash' ) {
191
- // Get the Post ID
192
- $ID = get_the_ID();
193
-
194
- // Get the permalink. If does not exist, then it is not an imported item.
195
- $permalink = get_post_meta( $ID, 'wprss_item_permalink', TRUE );
196
- if ( $permalink === '' ) {
197
- return $actions;
198
- }
199
-
200
- // The post type on the current screen
201
- $post_type = get_post_type();
202
- // Prepare the blacklist URL
203
- $plain_url = apply_filters(
204
- 'wprss_blacklist_row_action_url',
205
- admin_url( "edit.php?post_type=$post_type&wprss_blacklist=$ID" ),
206
- $ID
207
- ) . $paged;
208
- // Add a nonce to the URL
209
- $nonced_url = wp_nonce_url( $plain_url, 'blacklist-item-' . $ID, 'wprss_blacklist_item' );
210
-
211
- // Prepare the text
212
- $text = apply_filters( 'wprss_blacklist_row_action_text', htmlentities( __( 'Delete Permanently & Blacklist', WPRSS_TEXT_DOMAIN ) ) );
213
- $text = __( $text, WPRSS_TEXT_DOMAIN );
214
-
215
- // Prepare the hint
216
- $hint = apply_filters(
217
- 'wprss_blacklist_row_action_hint',
218
- __( 'The item will be deleted permanently, and its permalink will be recorded in the blacklist', WPRSS_TEXT_DOMAIN )
219
- );
220
- $hint = esc_attr( __( $hint, WPRSS_TEXT_DOMAIN ) );
221
-
222
- // Add the blacklist action
223
- $actions['blacklist-item'] = "<span class='delete'><a title='$hint' href='$nonced_url'>$text</a></span>";
224
- }
225
-
226
- // For the blacklisted item
227
- elseif ( get_post_type() === 'wprss_blacklist' ) {
228
- $paged = isset( $_GET['paged'] )? '&paged=' . $_GET['paged'] : '';
229
- $remove_url = wp_nonce_url( 'post.php?wprss-blacklist-remove='.get_the_ID(), 'blacklist-remove-' . get_the_ID(), 'wprss_blacklist_trash' );
230
- $actions = array(
231
- 'edit' => $actions['edit'],
232
- 'trash' => '<a href="'.$remove_url.'">' . __( 'Remove From Blacklist', WPRSS_TEXT_DOMAIN ) . '</a>'
233
- );
234
- }
235
-
236
- // Return the actions
237
- return $actions;
238
- }
239
-
240
- /**
241
- * Checks for the GET parameter wprss-blacklist-remove, and if present,
242
- * deletes the appropriate blacklist entry. Uses nonce 'wprss_blacklist_trash'
243
- * with action 'blacklist-remove-$ID'
244
  *
245
- * @since 4.4
246
  */
247
- function wprss_check_if_blacklist_delete() {
248
- // If the GET param is not set, do nothing. Return.
249
- if ( !array_key_exists('wprss-blacklist-remove', $_GET) ) return;
250
-
251
- // The array of blacklist entries to delete
252
- $to_delete = array();
253
- // The ID of the blacklist entry - if only deleting a single entry
254
- $ID = $_GET['wprss-blacklist-remove'];
255
-
256
- // check if deleting in bulk
257
- if ( isset( $_GET['wprss-bulk'] ) && $_GET['wprss-bulk'] == '1' ) {
258
- $to_delete = explode( ',', $ID );
259
- check_admin_referer( 'blacklist-remove-selected', 'wprss_blacklist_trash' );
260
- } else {
261
- $to_delete = array( $ID );
262
- // Get the ID from the GET param
263
- // Verify the nonce
264
- check_admin_referer( 'blacklist-remove-' . $ID, 'wprss_blacklist_trash' );
265
- }
266
-
267
- // Delete the posts marked for delete
268
- foreach( $to_delete as $delete_id ) {
269
- $post = get_post( $delete_id );
270
- if ( $post === NULL || get_post_type( $post ) !== 'wprss_blacklist' ) continue;
271
- wp_delete_post( $delete_id, TRUE );
272
- }
273
 
274
- // Redirect back to blacklists page
275
- $paged = isset( $_GET['paged'] )? '&paged=' . $_GET['paged'] : '';
276
- header('Location: ' . admin_url('edit.php?post_type=wprss_blacklist' . $paged ) );
277
- exit;
278
- }
279
 
280
- /**
281
- * Returns the custom columns for the blacklist post type
282
- *
283
- * @since 4.4
284
- * @params array $cols The columns to filter
285
- * @return array The new columns
286
- */
287
- function wprss_blacklist_columns( $cols ) {
288
- return array(
289
- 'cb' => $cols['cb'],
290
- 'title' => __( 'Title' ),
291
- 'permalink' => __( 'URL' )
292
- );
293
- }
294
 
295
- /**
296
- * Prints the cell data in the table for each blacklist entry
297
- *
298
- * @since 4.4
299
- * @param string $column The column slug
300
- * @param string|int $ID The ID of the post currently being printed
301
- */
302
- function wprss_blacklist_table_contents( $column, $ID ) {
303
- switch ( $column ) {
304
- case 'permalink':
305
- $permalink = get_post_meta( $ID, 'wprss_permalink', TRUE );
306
- echo '<a href="'.$permalink.'" target="_blank">'.$permalink.'</a>';
307
- break;
308
- }
309
- }
 
 
 
 
 
 
 
310
 
311
- /**
312
- * Removes the bulk actions for the Blacklist post type
313
- *
314
- * @since 4.4
315
- * @param array $actions The array of actions to be filtered
316
- * @return array An empty array
317
- */
318
- function wprss_blacklist_bulk_actions( $actions ) {
319
- return array();
320
- }
321
 
322
- /**
323
- * Adds the permalink meta box for the Blacklist post type.
324
- *
325
- * @since 4.13
326
- */
327
- function wprss_blacklist_add_meta_boxes()
328
- {
329
- add_meta_box(
330
- 'wprss-blacklist-permalink',
331
- __('Details', 'wprss'),
332
- function ($post) {
333
- echo wpra_get('twig')->load('admin/blacklist-metabox.twig')->render([
334
- 'value' => $post->wprss_permalink
335
- ]);
336
- },
337
- 'wprss_blacklist'
338
- );
339
  }
1
  <?php
2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3
  // Check if the 'blacklist' GET param is set
4
+ add_action('admin_init', 'wprss_check_if_blacklist_item');
5
  // Checks if the transient is set to show the notice
6
+ add_action('admin_init', 'wprss_check_notice_transient');
7
  // Register custom post type
8
  // Add the row actions to the targetted post type
9
+ add_filter('post_row_actions', 'wprss_blacklist_row_actions', 10, 1);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
  /**
12
  * Creates a blacklist entry for the given feed item.
13
  *
14
  * @since 4.4
15
+ *
16
  * @param int|string The ID of the feed item to add to the blacklist
17
  */
18
+ function wprss_blacklist_item($ID)
19
+ {
20
+ // Return if feed item is null
21
+ if (is_null($ID)) {
22
+ return;
23
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
24
 
25
+ // Get the feed item data
26
+ $item_title = get_the_title($ID);
27
+ $item_permalink = get_post_meta($ID, 'wprss_item_permalink', true);
28
+ // If not an imported item, stop
29
+ if ($item_permalink === '') {
30
+ wpra_get_logger()->warning('Feed item with ID {0} was not blacklisted because its URL was empty', [$ID]);
31
 
32
+ return;
33
+ }
34
+ // Prepare the data for blacklisting
35
+ $title = apply_filters('wprss_blacklist_title', trim($item_title));
36
+ $permalink = apply_filters('wprss_blacklist_permalink', trim($item_permalink));
37
+
38
+ // Delete the item
39
+ wp_delete_post($ID, true);
40
+
41
+ // Add the blacklisted item
42
+ $id = wp_insert_post([
43
+ 'post_title' => $title,
44
+ 'post_type' => 'wprss_blacklist',
45
+ 'post_status' => 'publish',
46
+ 'meta_input' => [
47
+ 'wprss_permalink' => $permalink,
48
+ ],
49
+ ]);
50
  }
51
 
52
  /**
53
  * Determines whether the given item is blacklist.
54
  *
55
  * @since 4.4
56
+ *
57
  * @param string $permalink The permalink to look for in the saved option
58
+ *
59
  * @return bool TRUE if the permalink is found, FALSE otherwise.
60
  */
61
+ function wprss_is_blacklisted($permalink)
62
+ {
63
  if (empty($permalink)) {
64
  return false;
65
  }
66
 
67
+ // Query the blacklist entries, for an item with the given permalink
68
+ $query = new WP_Query([
69
+ 'post_type' => 'wprss_blacklist',
70
+ 'meta_key' => 'wprss_permalink',
71
+ 'meta_value' => $permalink,
72
+ ]);
73
 
74
+ // Return TRUE if the query returned a result, FALSE otherwise
75
+ return $query->have_posts();
76
  }
77
 
78
  /**
80
  *
81
  * @since 4.4
82
  */
83
+ function wprss_check_if_blacklist_item()
84
+ {
85
+ // If the GET param is not set, do nothing. Return.
86
+ if (empty($_GET['wprss_blacklist'])) {
87
+ return;
88
+ }
89
 
90
+ // Get the ID from the GET param
91
+ $ID = $_GET['wprss_blacklist'];
92
+ // If the post does not exist, stop. Show a message
93
+ if (get_post($ID) === null) {
94
+ wp_die(__('The item you are trying to blacklist does not exist', 'wprss'));
95
+ }
96
 
97
+ // If the post type is not correct,
98
+ if (get_post_meta($ID, 'wprss_item_permalink', true) === '' || get_post_status($ID) !== 'trash') {
99
+ wp_die(__('The item you are trying to blacklist is not valid!', 'wprss'));
100
+ }
101
 
102
+ check_admin_referer('blacklist-item-' . $ID, 'wprss_blacklist_item');
103
+ wprss_blacklist_item($ID);
104
 
105
+ // Get the current post type for the current page
106
+ $post_type = isset($_GET['post_type']) ? $_GET['post_type'] : 'post';
107
+ // Check the current page, and generate the URL query string for the page
108
+ $paged = isset($_GET['paged']) ? '&paged=' . $_GET['paged'] : '';
109
+ // Set the notice transient
110
+ set_transient('wprss_item_blacklist_notice', 'true');
111
+ // Refresh the page without the GET parameter
112
+ wp_redirect(admin_url("edit.php?post_type=$post_type&post_status=trash" . $paged));
113
+
114
+ exit;
115
  }
116
 
117
  /**
118
  * Checks if the transient for the blacklist notice is set, and shows the notice
119
  * if it is set.
120
  */
121
+ function wprss_check_notice_transient()
122
+ {
123
+ // Check if the transient exists
124
+ $transient = get_transient('wprss_item_blacklist_notice');
125
+ if ($transient !== false) {
126
+ // Remove the transient
127
+ delete_transient('wprss_item_blacklist_notice');
128
+ // Show the notice
129
+ // add_action( 'admin_notices', 'wprss_blacklist_item_notice' );
130
  wprss()->getAdminAjaxNotices()->addNotice('blacklist_item_success');
131
+ }
132
  }
133
 
134
  /**
136
  * Default post type = wprss_feed_item
137
  *
138
  * @since 4.4
139
+ *
140
  * @param array $actions The row actions to be filtered
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
141
  *
142
+ * @return array The new filtered row actions
143
  */
144
+ function wprss_blacklist_row_actions($actions)
145
+ {
146
+ // Check the current page, and generate the URL query string for the page
147
+ $paged = isset($_GET['paged'])
148
+ ? sprintf('&paged=%s', $_GET['paged'])
149
+ : '';
150
+
151
+ // Check the post type
152
+ if (get_post_status() !== 'trash') {
153
+ return $actions;
154
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
155
 
156
+ // Get the Post ID
157
+ $ID = get_the_ID();
 
 
 
158
 
159
+ // Get the permalink. If does not exist, then it is not an imported item.
160
+ $permalink = get_post_meta($ID, 'wprss_item_permalink', true);
161
+ if ($permalink === '') {
162
+ return $actions;
163
+ }
 
 
 
 
 
 
 
 
 
164
 
165
+ // The post type on the current screen
166
+ $post_type = get_post_type();
167
+ // Prepare the blacklist URL
168
+ $plain_url = apply_filters(
169
+ 'wprss_blacklist_row_action_url',
170
+ admin_url("edit.php?post_type=$post_type&wprss_blacklist=$ID"),
171
+ $ID
172
+ );
173
+ $plain_url = $plain_url . $paged;
174
+ // Add a nonce to the URL
175
+ $nonced_url = wp_nonce_url($plain_url, 'blacklist-item-' . $ID, 'wprss_blacklist_item');
176
+
177
+ // Prepare the text
178
+ $text = htmlentities(__('Delete Permanently & Blacklist', 'wprss'));
179
+ $text = apply_filters('wprss_blacklist_row_action_text', $text);
180
+
181
+ // Prepare the hint
182
+ $hint = apply_filters(
183
+ 'wprss_blacklist_row_action_hint',
184
+ __('The item will be deleted permanently, and its permalink will be recorded in the blacklist', 'wprss')
185
+ );
186
+ $hint = esc_attr(__($hint, 'wprss'));
187
 
188
+ // Add the blacklist action
189
+ $actions['blacklist-item'] = "<span class='delete'><a title='$hint' href='$nonced_url'>$text</a></span>";
 
 
 
 
 
 
 
 
190
 
191
+ return $actions;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
192
  }
includes/feed-importing-images.php CHANGED
@@ -702,147 +702,153 @@ function wpra_media_sideload_image($url = null, $post_id = null, $attach = null,
702
  return new WP_Error('missing', "Need a valid URL and post ID...");
703
  }
704
 
705
- if (!wpra_container()->has('wpra/images/container')) {
706
- return new WP_Error('Images module is not loaded');
707
- }
 
 
 
 
 
 
 
 
 
 
708
 
709
- $logger = wpra_get_logger();
710
- $images = wpra_container()->get('wpra/images/container');
711
 
712
- try {
713
- /* @var $img WPRSS_Image_Cache_Image */
714
- $img = $images->get($url);
715
- } catch (Exception $e) {
716
- return new WP_Error('could_not_load_image', $e->getMessage(), $url);
717
- }
718
 
719
- $logger->debug('Image from cache: {url} -> {path}', [
720
- 'url' => $img->get_url(),
721
- 'path' => $img->get_local_path(),
722
- ]);
723
 
724
- // Get the path
725
- $tmp = $img->get_local_path();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
726
 
727
- // Required for wp_tempnam() function
728
- require_once(ABSPATH . 'wp-admin/includes/file.php');
729
 
730
- // media_handle_sideload() will move the file, but we need the cache to remain
731
- copy($tmp, $tmp = wp_tempnam());
732
- $tmpPath = pathinfo($tmp);
733
- $ext = isset($tmpPath['extension']) ? trim($tmpPath['extension']) : null;
734
- $url_filename = $img->get_unique_name();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
735
 
736
- $logger->debug('Copied cached image to {path}', [
737
- 'path' => $tmp,
738
- ]);
739
 
740
- // override filename if given, reconstruct server path
741
- if (!empty($filename)) {
742
- $filename = sanitize_file_name($filename);
743
- // build new path
744
- $new = $tmpPath['dirname'] . "/" . $filename . "." . $ext;
745
- // renames temp file on server
746
- rename($tmp, $new);
747
- // push new filename (in path) to be used in file array later
748
- $tmp = $new;
749
- }
750
-
751
- // determine file type (ext and mime/type)
752
- $url_type = wp_check_filetype($url_filename);
753
-
754
- // If the wp_check_filetype function fails to determine the MIME type
755
- if (empty($url_type['type'])) {
756
- $url_type = wpra_check_file_type($tmp, $url);
757
- }
758
- $ext = $url_type['ext'];
759
-
760
- // assemble file data (should be built like $_FILES since wp_handle_sideload() will be using)
761
- $file_array = [];
762
- // full server path to temp file
763
- $file_array['tmp_name'] = $tmp;
764
- $url = trim($img->get_url());
765
- $parts = parse_url($url);
766
- $baseName = uniqid($parts['host']);
767
-
768
- if (!empty($filename)) {
769
- // user given filename for title, add original URL extension
770
- $baseName = $filename . "." . $ext;
771
- } else {
772
- // The original basename, falling back to auto-generated based on domain
773
- $base = basename($parts['path']);
774
- if (strlen($baseName) || trim($baseName) !== '/') {
775
- $baseName = $base;
776
  }
777
- }
778
 
779
- $file_array['name'] = $baseName;
 
 
 
780
 
781
- // set additional wp_posts columns
782
- if (empty($post_data['post_title'])) {
783
- // just use the original filename (no extension)
784
- $post_data['post_title'] = $file_array['name'];
785
- }
786
 
787
- // make sure gets tied to parent
788
- if (empty($post_data['post_parent'])) {
789
- $post_data['post_parent'] = $post_id;
790
- }
791
 
792
- // required files for WP media_handle_sideload
793
- require_once(ABSPATH . 'wp-admin/includes/media.php');
794
- require_once(ABSPATH . 'wp-admin/includes/image.php');
 
795
 
796
- // NO FILENAME FIX
797
- // WordPress does not allow file images that are not in the form of a filename
798
- // ex: http://domain.com/thoufiqadsjucpqwuamoshfjnax8mtrh/iorqhewufjasj
 
799
 
800
- if (apply_filters('wpra_override_upload_security', true) === true) {
801
- // If we successfully retrieved the MIME type
802
- if ($url_type !== false && isset($url_type['type']) && !empty($url_type['type'])) {
803
- $mime_to_ext = wpra_get_mime_type_ext_mapping();
 
804
 
805
- $mime_type = $url_type['type'];
806
- $file_ext = isset($mime_to_ext[$mime_type])
807
- ? $mime_to_ext[$mime_type]
808
- : null;
809
 
810
- // If no file extension, check if the mime type begins with "image/" and if so default to "png"
811
- $mime_type_parts = explode('/', $mime_type);
812
- if ($file_ext === null && count($mime_type_parts) > 1 && $mime_type_parts[0] === 'image') {
813
- $file_ext = 'png';
814
  }
815
-
816
- // Add a filter to ensure that the image ext and mime type get passed through WordPress' security
817
- add_filter('wp_check_filetype_and_ext', function ($image) use ($file_ext, $mime_type) {
818
- $image['ext'] = empty($image['ext']) ? $file_ext : $image['ext'];
819
- $image['type'] = empty($image['type']) ? $mime_type : $image['type'];
820
-
821
- return $image;
822
- }, 10);
823
  }
824
- }
825
 
826
- // do the validation and storage stuff
827
- // For some reason, deep down filesize() returned 0 for the temporary file without this
828
- clearstatcache(false, $file_array['tmp_name']);
829
 
830
- // $post_data can override the items saved to wp_posts table,
831
- // like post_mime_type, guid, post_parent, post_title, post_content, post_status
832
- $att_id = media_handle_sideload( $file_array, $post_id, '', $post_data);
833
 
834
- // If error storing permanently, unlink
835
- if (is_wp_error($att_id)) {
836
- $logger->warning('Failed to download and attach image to post #{id}. Image URL: {url}', [
837
- 'id' => $post_id,
838
- 'url' => $url,
839
- ]);
840
 
841
- // Delete the cache copy needed for media_handle_sideload()
842
- $img->delete();
843
- @unlink($tmp);
844
 
845
- return $att_id;
 
846
  }
847
 
848
  // set as post thumbnail if desired
@@ -850,6 +856,9 @@ function wpra_media_sideload_image($url = null, $post_id = null, $attach = null,
850
  set_post_thumbnail( $post_id, $att_id );
851
  }
852
 
 
 
 
853
  return $att_id;
854
  }
855
 
702
  return new WP_Error('missing', "Need a valid URL and post ID...");
703
  }
704
 
705
+ // Check if the image already exists in the media library
706
+ $existing = get_posts([
707
+ 'post_type' => 'attachment',
708
+ 'meta_key' => 'wprss_og_image_url',
709
+ 'meta_value' => $url,
710
+ ]);
711
+ // If so, use the existing image's ID
712
+ if (!empty($existing)) {
713
+ $att_id = reset($existing)->ID;
714
+ } else {
715
+ if (!wpra_container()->has('wpra/images/container')) {
716
+ return new WP_Error('Images module is not loaded');
717
+ }
718
 
719
+ $logger = wpra_get_logger();
720
+ $images = wpra_container()->get('wpra/images/container');
721
 
722
+ try {
723
+ /* @var $img WPRSS_Image_Cache_Image */
724
+ $img = $images->get($url);
725
+ } catch (Exception $e) {
726
+ return new WP_Error('could_not_load_image', $e->getMessage(), $url);
727
+ }
728
 
729
+ $logger->debug('Image from cache: {url} -> {path}', [
730
+ 'url' => $img->get_url(),
731
+ 'path' => $img->get_local_path(),
732
+ ]);
733
 
734
+ // Get the path
735
+ $tmp = $img->get_local_path();
736
+
737
+ // Required for wp_tempnam() function
738
+ require_once(ABSPATH . 'wp-admin/includes/file.php');
739
+
740
+ // media_handle_sideload() will move the file, but we need the cache to remain
741
+ copy($tmp, $tmp = wp_tempnam());
742
+ $tmpPath = pathinfo($tmp);
743
+ $ext = isset($tmpPath['extension']) ? trim($tmpPath['extension']) : null;
744
+ $url_filename = $img->get_unique_name();
745
+
746
+ // override filename if given, reconstruct server path
747
+ if (!empty($filename)) {
748
+ $filename = sanitize_file_name($filename);
749
+ // build new path
750
+ $new = $tmpPath['dirname'] . "/" . $filename . "." . $ext;
751
+ // renames temp file on server
752
+ rename($tmp, $new);
753
+ // push new filename (in path) to be used in file array later
754
+ $tmp = $new;
755
+ }
756
 
757
+ // determine file type (ext and mime/type)
758
+ $url_type = wp_check_filetype($url_filename);
759
 
760
+ // If the wp_check_filetype function fails to determine the MIME type
761
+ if (empty($url_type['type'])) {
762
+ $url_type = wpra_check_file_type($tmp, $url);
763
+ }
764
+ $ext = $url_type['ext'];
765
+
766
+ // assemble file data (should be built like $_FILES since wp_handle_sideload() will be using)
767
+ $file_array = [];
768
+ // full server path to temp file
769
+ $file_array['tmp_name'] = $tmp;
770
+ $parts = parse_url(trim($img->get_url()));
771
+ $baseName = uniqid($parts['host']);
772
+
773
+ if (!empty($filename)) {
774
+ // user given filename for title, add original URL extension
775
+ $baseName = $filename . "." . $ext;
776
+ } else {
777
+ // The original basename, falling back to auto-generated based on domain
778
+ $base = basename($parts['path']);
779
+ if (strlen($baseName) || trim($baseName) !== '/') {
780
+ $baseName = $base;
781
+ }
782
+ }
783
 
784
+ $file_array['name'] = $baseName;
 
 
785
 
786
+ // set additional wp_posts columns
787
+ if (empty($post_data['post_title'])) {
788
+ // just use the original filename (no extension)
789
+ $post_data['post_title'] = $file_array['name'];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
790
  }
 
791
 
792
+ // make sure gets tied to parent
793
+ if (empty($post_data['post_parent'])) {
794
+ $post_data['post_parent'] = $post_id;
795
+ }
796
 
797
+ // required files for WP media_handle_sideload
798
+ require_once(ABSPATH . 'wp-admin/includes/media.php');
799
+ require_once(ABSPATH . 'wp-admin/includes/image.php');
 
 
800
 
801
+ // NO FILENAME FIX
802
+ // WordPress does not allow file images that are not in the form of a filename
803
+ // ex: http://domain.com/thoufiqadsjucpqwuamoshfjnax8mtrh/iorqhewufjasj
 
804
 
805
+ if (apply_filters('wpra_override_upload_security', true) === true) {
806
+ // If we successfully retrieved the MIME type
807
+ if ($url_type !== false && isset($url_type['type']) && !empty($url_type['type'])) {
808
+ $mime_to_ext = wpra_get_mime_type_ext_mapping();
809
 
810
+ $mime_type = $url_type['type'];
811
+ $file_ext = isset($mime_to_ext[$mime_type])
812
+ ? $mime_to_ext[$mime_type]
813
+ : null;
814
 
815
+ // If no file extension, check if the mime type begins with "image/" and if so default to "png"
816
+ $mime_type_parts = explode('/', $mime_type);
817
+ if ($file_ext === null && count($mime_type_parts) > 1 && $mime_type_parts[0] === 'image') {
818
+ $file_ext = 'png';
819
+ }
820
 
821
+ // Add a filter to ensure that the image ext and mime type get passed through WordPress' security
822
+ add_filter('wp_check_filetype_and_ext', function ($image) use ($file_ext, $mime_type) {
823
+ $image['ext'] = empty($image['ext']) ? $file_ext : $image['ext'];
824
+ $image['type'] = empty($image['type']) ? $mime_type : $image['type'];
825
 
826
+ return $image;
827
+ }, 10);
 
 
828
  }
 
 
 
 
 
 
 
 
829
  }
 
830
 
831
+ // do the validation and storage stuff
832
+ // For some reason, deep down filesize() returned 0 for the temporary file without this
833
+ clearstatcache(false, $file_array['tmp_name']);
834
 
835
+ // $post_data can override the items saved to wp_posts table,
836
+ // like post_mime_type, guid, post_parent, post_title, post_content, post_status
837
+ $att_id = media_handle_sideload($file_array, $post_id, '', $post_data);
838
 
839
+ // If error storing permanently, unlink
840
+ if (is_wp_error($att_id)) {
841
+ $logger->warning('Failed to download and attach image to post #{id}. Image URL: {url}', [
842
+ 'id' => $post_id,
843
+ 'url' => $url,
844
+ ]);
845
 
846
+ // Delete the cache copy needed for media_handle_sideload()
847
+ $img->delete();
848
+ @unlink($tmp);
849
 
850
+ return $att_id;
851
+ }
852
  }
853
 
854
  // set as post thumbnail if desired
856
  set_post_thumbnail( $post_id, $att_id );
857
  }
858
 
859
+ // Save the original image URL in the attachment's meta data
860
+ update_post_meta($att_id, 'wprss_og_image_url', $url);
861
+
862
  return $att_id;
863
  }
864
 
includes/feed-importing.php CHANGED
@@ -70,8 +70,6 @@
70
  else $feed_limit = $global_limit;
71
  }
72
 
73
- $logger->debug('Feed item import limit: {0}', [$feed_limit]);
74
-
75
  // Filter the URL for validaty
76
  if ( ! wprss_validate_url( $feed_url ) ) {
77
  $logger->error('Feed URL is not valid!');
@@ -91,7 +89,7 @@
91
  $items_to_insert = $items;
92
  } else {
93
  $items_to_insert = array_slice( $items, 0, $feed_limit );
94
- $logger->info('Fetched {0} items. Got {1} items after applying limit', [
95
  count($items),
96
  count($items_to_insert)
97
  ]);
@@ -114,9 +112,7 @@
114
  $new_items = array();
115
  foreach ( $items_to_insert as $item ) {
116
  $item_title = $item->get_title();
117
-
118
  $permalink = wprss_normalize_permalink( $item->get_permalink(), $item, $feed_ID );
119
- $logger->debug('Checking item "{0}"', [$item_title]);
120
 
121
  // Check if blacklisted
122
  if (wprss_is_blacklisted($permalink)) {
@@ -160,7 +156,6 @@
160
  $items_to_insert = $new_items;
161
  $per_import = wprss_get_general_setting('limit_feed_items_per_import');
162
  if (!empty($per_import)) {
163
- $logger->debug('Applying per-import item limit of {0} items', [$per_import]);
164
  $items_to_insert = array_slice( $items_to_insert, 0, $per_import );
165
  }
166
 
@@ -191,7 +186,7 @@
191
  }
192
 
193
  if ($num_items_deleted > 0) {
194
- $logger->info('Deleted the oldest {0} items from the database', [$num_items_deleted]);
195
  }
196
  }
197
 
@@ -562,22 +557,16 @@ function wprss_get_feed_cache_dir()
562
  $permalink = $item->get_permalink(); // Link or enclosure URL
563
  $permalink = htmlspecialchars_decode( $permalink ); // SimplePie encodes HTML special chars
564
 
565
- $logger->debug('Beginning import for "{0}"', [$item->get_title()]);
566
 
567
  $permalink = wprss_normalize_permalink( $permalink, $item, $feed_ID );
568
 
569
  // Save the enclosure URL
570
  $enclosure_url = '';
571
- if ( $enclosure = $item->get_enclosure(0) ) {
572
 
573
- if ( $enclosure->get_link() ) {
574
- $enclosure_url = $enclosure->get_link();
575
-
576
- $logger->debug('Item "{0}" has an enclosure link: {1}', [
577
- $item->get_title(),
578
- $enclosure_url
579
- ]);
580
- }
581
  }
582
 
583
  // Check if newly fetched item already present in existing feed items,
@@ -588,6 +577,7 @@ function wprss_get_feed_cache_dir()
588
  set_time_limit( $time_limit );
589
 
590
  // Apply filters that determine if the feed item should be inserted into the DB or not.
 
591
  $item = apply_filters( 'wprss_insert_post_item_conditionals', $item, $feed_ID, $permalink );
592
 
593
  // Check if the imported count should still be updated, even if the item is NULL
@@ -595,14 +585,27 @@ function wprss_get_feed_cache_dir()
595
 
596
  // If the item is not NULL, continue to inserting the feed item post into the DB
597
  if ( $item !== NULL && !is_bool($item) ) {
 
 
598
  // Get the date and GTM date and normalize if not valid dor not present
599
  $format = 'Y-m-d H:i:s';
600
  $has_date = $item->get_date( 'U' ) ? TRUE : FALSE;
601
  $timestamp = $has_date ? $item->get_date( 'U' ) : date( 'U' );
602
 
603
- if (apply_filters('wpra/importer/allow_scheduled_items', false) !== true) {
604
- $timestamp = min(time() - $i, $timestamp);
605
- }
 
 
 
 
 
 
 
 
 
 
 
606
 
607
  $date = date( $format, $timestamp );
608
  $date_gmt = gmdate( $format, $timestamp );
@@ -619,7 +622,7 @@ function wprss_get_feed_cache_dir()
619
  'post_title' => html_entity_decode( $item->get_title() ),
620
  'post_content' => $item->get_content(),
621
  'post_excerpt' => wprss_sanitize_excerpt($item->get_description()),
622
- 'post_status' => 'publish',
623
  'post_type' => 'wprss_feed_item',
624
  'post_date' => $date,
625
  'post_date_gmt' => $date_gmt
@@ -631,8 +634,6 @@ function wprss_get_feed_cache_dir()
631
  @include_once( WP_PLUGIN_DIR . '/sitepress-multilingual-cms/inc/wpml-api.php' );
632
  if ( defined('ICL_LANGUAGE_CODE') ) {
633
  $_POST['icl_post_language'] = $language_code = ICL_LANGUAGE_CODE;
634
-
635
- $logger->debug('Detected WPML with language code {0]', [$language_code]);
636
  }
637
 
638
  // Create and insert post object into the DB
@@ -668,12 +669,10 @@ function wprss_get_feed_cache_dir()
668
  // increment the inserted counter
669
  elseif ( ( is_bool($item) && $item === TRUE ) || ( $still_update_count === TRUE && $item !== FALSE ) ) {
670
  $items_inserted++;
671
- }
 
 
672
  }
673
-
674
- if (is_object($item) && !is_wp_error($item)) {
675
- $logger->info('Imported "{0}"', [$item->get_title()]);
676
- }
677
  }
678
 
679
  update_post_meta( $feed_ID, 'wprss_last_update_items', $items_inserted );
70
  else $feed_limit = $global_limit;
71
  }
72
 
 
 
73
  // Filter the URL for validaty
74
  if ( ! wprss_validate_url( $feed_url ) ) {
75
  $logger->error('Feed URL is not valid!');
89
  $items_to_insert = $items;
90
  } else {
91
  $items_to_insert = array_slice( $items, 0, $feed_limit );
92
+ $logger->debug('{0} items in the feed, {1} items after applying limit', [
93
  count($items),
94
  count($items_to_insert)
95
  ]);
112
  $new_items = array();
113
  foreach ( $items_to_insert as $item ) {
114
  $item_title = $item->get_title();
 
115
  $permalink = wprss_normalize_permalink( $item->get_permalink(), $item, $feed_ID );
 
116
 
117
  // Check if blacklisted
118
  if (wprss_is_blacklisted($permalink)) {
156
  $items_to_insert = $new_items;
157
  $per_import = wprss_get_general_setting('limit_feed_items_per_import');
158
  if (!empty($per_import)) {
 
159
  $items_to_insert = array_slice( $items_to_insert, 0, $per_import );
160
  }
161
 
186
  }
187
 
188
  if ($num_items_deleted > 0) {
189
+ $logger->info('Deleted the oldest {0} items', [$num_items_deleted]);
190
  }
191
  }
192
 
557
  $permalink = $item->get_permalink(); // Link or enclosure URL
558
  $permalink = htmlspecialchars_decode( $permalink ); // SimplePie encodes HTML special chars
559
 
560
+ $logger->debug('Saving item "{0}"', [$item->get_title()]);
561
 
562
  $permalink = wprss_normalize_permalink( $permalink, $item, $feed_ID );
563
 
564
  // Save the enclosure URL
565
  $enclosure_url = '';
566
+ $enclosure = $item->get_enclosure(0);
567
 
568
+ if ($enclosure && $enclosure->get_link()) {
569
+ $enclosure_url = $enclosure->get_link();
 
 
 
 
 
 
570
  }
571
 
572
  // Check if newly fetched item already present in existing feed items,
577
  set_time_limit( $time_limit );
578
 
579
  // Apply filters that determine if the feed item should be inserted into the DB or not.
580
+ $ogItem = $item;
581
  $item = apply_filters( 'wprss_insert_post_item_conditionals', $item, $feed_ID, $permalink );
582
 
583
  // Check if the imported count should still be updated, even if the item is NULL
585
 
586
  // If the item is not NULL, continue to inserting the feed item post into the DB
587
  if ( $item !== NULL && !is_bool($item) ) {
588
+ $post_status = 'publish';
589
+
590
  // Get the date and GTM date and normalize if not valid dor not present
591
  $format = 'Y-m-d H:i:s';
592
  $has_date = $item->get_date( 'U' ) ? TRUE : FALSE;
593
  $timestamp = $has_date ? $item->get_date( 'U' ) : date( 'U' );
594
 
595
+ // Item has a future timestamp
596
+ if ($timestamp > time()) {
597
+ $schedule_items_filter = apply_filters('wpra/importer/allow_scheduled_items', false);
598
+ $schedule_items_option = wprss_get_general_setting('schedule_future_items');
599
+
600
+ if ($schedule_items_filter || $schedule_items_option) {
601
+ // If can schedule future items, set the post status to "future" (aka scheduled)
602
+ $post_status = 'future';
603
+ } else {
604
+ // If cannot schedule future items, clamp the timestamp to the currrent time minus
605
+ // 1 second for each iteration done so far
606
+ $timestamp = min(time() - $i, $timestamp);
607
+ }
608
+ }
609
 
610
  $date = date( $format, $timestamp );
611
  $date_gmt = gmdate( $format, $timestamp );
622
  'post_title' => html_entity_decode( $item->get_title() ),
623
  'post_content' => $item->get_content(),
624
  'post_excerpt' => wprss_sanitize_excerpt($item->get_description()),
625
+ 'post_status' => $post_status,
626
  'post_type' => 'wprss_feed_item',
627
  'post_date' => $date,
628
  'post_date_gmt' => $date_gmt
634
  @include_once( WP_PLUGIN_DIR . '/sitepress-multilingual-cms/inc/wpml-api.php' );
635
  if ( defined('ICL_LANGUAGE_CODE') ) {
636
  $_POST['icl_post_language'] = $language_code = ICL_LANGUAGE_CODE;
 
 
637
  }
638
 
639
  // Create and insert post object into the DB
669
  // increment the inserted counter
670
  elseif ( ( is_bool($item) && $item === TRUE ) || ( $still_update_count === TRUE && $item !== FALSE ) ) {
671
  $items_inserted++;
672
+ } else {
673
+ $logger->error('Failed to save item "{0}"', [$ogItem->get_title()]);
674
+ }
675
  }
 
 
 
 
676
  }
677
 
678
  update_post_meta( $feed_ID, 'wprss_last_update_items', $items_inserted );
includes/feed-processing.php CHANGED
@@ -74,7 +74,7 @@
74
  $args = apply_filters(
75
  'wprss_get_feed_items_for_source_args',
76
  array(
77
- 'post_type' => 'wprss_feed_item',
78
  'cache_results' => false, // Disable caching, used for one-off queries
79
  'no_found_rows' => true, // We don't need pagination, so disable it
80
  'posts_per_page' => -1,
@@ -376,22 +376,27 @@
376
  * @since 3.0
377
  */
378
  function wprss_delete_all_feed_items() {
379
- wprss_log( sprintf( 'Deleting all feed items...'), __FUNCTION__, WPRSS_LOG_LEVEL_SYSTEM );
380
- $args = array(
381
- 'post_type' => 'wprss_feed_item',
382
- 'cache_results' => false, // Disable caching, used for one-off queries
383
- 'no_found_rows' => true, // We don't need pagination, so disable it
384
- 'fields' => 'ids', // Returns post IDs only
385
- 'posts_per_page' => -1,
386
- );
387
-
388
- $feed_item_ids = get_posts( $args );
389
- foreach( $feed_item_ids as $feed_item_id ) {
390
- $purge = wp_delete_post( $feed_item_id, true ); // delete the feed item, skipping trash
 
 
 
 
 
 
391
  }
 
392
  wp_reset_postdata();
393
- wprss_log( sprintf( 'All feed items deleted: %1$d', count($feed_item_ids) ), __FUNCTION__, WPRSS_LOG_LEVEL_INFO );
394
- do_action('wprss_delete_all_feed_items_after', $feed_item_ids);
395
  }
396
 
397
 
@@ -715,13 +720,12 @@
715
 
716
 
717
  /**
718
- * Deletes all imported feeds and re-imports everything
719
  *
720
  * @since 3.0
721
  */
722
  function wprss_feed_reset() {
723
  wp_schedule_single_event( time(), 'wprss_delete_all_feed_items_hook' );
724
- set_transient( WPRSS_TRANSIENT_NAME_IS_REIMPORTING, true );
725
  }
726
 
727
 
@@ -734,7 +738,6 @@
734
  delete_transient( WPRSS_TRANSIENT_NAME_IS_REIMPORTING );
735
  wprss_fetch_insert_all_feed_items( TRUE );
736
  }
737
- add_action('wprss_delete_all_feed_items_after', 'wprss_schedule_reimport_all');
738
 
739
 
740
  /**
74
  $args = apply_filters(
75
  'wprss_get_feed_items_for_source_args',
76
  array(
77
+ 'post_type' => get_post_types(),
78
  'cache_results' => false, // Disable caching, used for one-off queries
79
  'no_found_rows' => true, // We don't need pagination, so disable it
80
  'posts_per_page' => -1,
376
  * @since 3.0
377
  */
378
  function wprss_delete_all_feed_items() {
379
+ $args = [
380
+ 'post_type' => get_post_types(),
381
+ 'suppress_filters' => true,
382
+ 'cache_results' => false,
383
+ 'fields' => 'ids',
384
+ 'posts_per_page' => -1,
385
+ 'meta_query' => [
386
+ 'relation' => 'AND',
387
+ [
388
+ 'key' => 'wprss_feed_id',
389
+ 'compare' => 'EXISTS',
390
+ ],
391
+ ],
392
+ ];
393
+
394
+ $items = get_posts($args);
395
+ foreach ($items as $item) {
396
+ $purge = wp_delete_post($item, true);
397
  }
398
+
399
  wp_reset_postdata();
 
 
400
  }
401
 
402
 
720
 
721
 
722
  /**
723
+ * Deletes all imported feeds.
724
  *
725
  * @since 3.0
726
  */
727
  function wprss_feed_reset() {
728
  wp_schedule_single_event( time(), 'wprss_delete_all_feed_items_hook' );
 
729
  }
730
 
731
 
738
  delete_transient( WPRSS_TRANSIENT_NAME_IS_REIMPORTING );
739
  wprss_fetch_insert_all_feed_items( TRUE );
740
  }
 
741
 
742
 
743
  /**
includes/functions.php CHANGED
@@ -284,3 +284,45 @@ if (!function_exists('array_pick')) {
284
  return array_intersect_key($array, array_flip($keys));
285
  }
286
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
284
  return array_intersect_key($array, array_flip($keys));
285
  }
286
  }
287
+
288
+ /**
289
+ * Retrieve cron jobs ready to be run.
290
+ *
291
+ * @since 4.17
292
+ *
293
+ * @return array Cron jobs ready to be run.
294
+ */
295
+ function wpra_get_ready_cron_jobs() {
296
+ if (function_exists('wp_get_ready_cron_jobs')) {
297
+ return wp_get_ready_cron_jobs();
298
+ }
299
+
300
+ $pre = apply_filters('pre_get_ready_cron_jobs', null);
301
+ if ($pre !== null) {
302
+ return $pre;
303
+ }
304
+
305
+ $crons = _get_cron_array();
306
+
307
+ if ($crons === false) {
308
+ return [];
309
+ }
310
+
311
+ $keys = array_keys($crons);
312
+ $gmt_time = microtime(true);
313
+
314
+ if (isset($keys[0]) && $keys[0] > $gmt_time) {
315
+ return [];
316
+ }
317
+
318
+ $results = [];
319
+ foreach ($crons as $timestamp => $cronhooks) {
320
+ if ($timestamp > $gmt_time) {
321
+ break;
322
+ }
323
+
324
+ $results[$timestamp] = $cronhooks;
325
+ }
326
+
327
+ return $results;
328
+ }
includes/image-caching.php CHANGED
@@ -514,6 +514,9 @@ class WPRSS_Image_Cache {
514
  throw new Exception( sprintf( __( 'Invalid URL provided: "%1$s"' ), $url ) );
515
  }
516
 
 
 
 
517
  if ( !is_null( $target_path ) ) {
518
  $path = $target_path;
519
  }
@@ -549,7 +552,7 @@ class WPRSS_Image_Cache {
549
  array(
550
  'timeout' => $timeout,
551
  'stream' => true,
552
- 'filename' => $tmpfname
553
  )
554
  );
555
 
@@ -1358,13 +1361,14 @@ class WPRSS_Image_Cache_Image {
1358
  if ( !$this->is_readable() ) throw new Exception( sprintf( '%1$s: image file is not readable', $path ) );
1359
 
1360
  // Trying simplest way
1361
- if ( $size = getimagesize( $path ) ) {
1362
- $this->_size = [0 => $size[0], 1 => $size[1]];
1363
- }
 
1364
 
1365
  if( !$this->_size && function_exists( 'gd_info' ) ) {
1366
  $image = file_get_contents( $path );
1367
- $image = imagecreatefromstring( $image );
1368
 
1369
  if ($image !== false) {
1370
  $width = imagesx($image);
514
  throw new Exception( sprintf( __( 'Invalid URL provided: "%1$s"' ), $url ) );
515
  }
516
 
517
+ // Since image URLs may be found from "src" attributes of <img> tags, HTML entities may need to be decoded
518
+ $url = html_entity_decode($url);
519
+
520
  if ( !is_null( $target_path ) ) {
521
  $path = $target_path;
522
  }
552
  array(
553
  'timeout' => $timeout,
554
  'stream' => true,
555
+ 'filename' => $tmpfname,
556
  )
557
  );
558
 
1361
  if ( !$this->is_readable() ) throw new Exception( sprintf( '%1$s: image file is not readable', $path ) );
1362
 
1363
  // Trying simplest way
1364
+ $size = @getimagesize( $path );
1365
+ if ( $size ) {
1366
+ $this->_size = [0 => $size[0], 1 => $size[1]];
1367
+ }
1368
 
1369
  if( !$this->_size && function_exists( 'gd_info' ) ) {
1370
  $image = file_get_contents( $path );
1371
+ $image = @imagecreatefromstring( $image );
1372
 
1373
  if ($image !== false) {
1374
  $width = imagesx($image);
includes/libraries/browser.php CHANGED
@@ -369,26 +369,10 @@ class Browser {
369
  * @return string formatted string with a summary of the browser
370
  */
371
  function __toString() {
372
- $text1 = $this->getUserAgent(); //grabs the UA (user agent) string
373
- $UAline1 = substr( $text1, 0, 32 ); //the first line we print should only be the first 32 characters of the UA string
374
- $text2 = $this->getUserAgent();//now we grab it again and save it to a string
375
- $towrapUA = str_replace( $UAline1, '', $text2 );//the rest of the printoff (other than first line) is equivolent
376
- // To the whole string minus the part we printed off. IE
377
- // User Agent: thefirst32charactersfromUAline1
378
- // the rest of it is now stored in
379
- // $text2 to be printed off
380
- // But we need to add spaces before each line that is split other than line 1
381
- $space = '';
382
- for ( $i = 0; $i < 25; $i++ ) {
383
- $space .= ' ';
384
- }
385
- // Now we split the remaining string of UA ($text2) into lines that are prefixed by spaces for formatting
386
- $wordwrapped = chunk_split( $towrapUA, 32, "\n $space" );
387
  return "Platform: {$this->getPlatform()} \n".
388
  "Browser Name: {$this->getBrowser()} \n" .
389
  "Browser Version: {$this->getVersion()} \n" .
390
- "User Agent String: $UAline1 \n\t\t\t " .
391
- "$wordwrapped";
392
  }
393
  /**
394
  * Protected routine to calculate and determine what the browser is in use (including platform)
369
  * @return string formatted string with a summary of the browser
370
  */
371
  function __toString() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
372
  return "Platform: {$this->getPlatform()} \n".
373
  "Browser Name: {$this->getBrowser()} \n" .
374
  "Browser Version: {$this->getVersion()} \n" .
375
+ "User Agent String: {$this->getUserAgent()}\n";
 
376
  }
377
  /**
378
  * Protected routine to calculate and determine what the browser is in use (including platform)
includes/scripts.php CHANGED
@@ -76,6 +76,14 @@
76
  ));
77
 
78
  wp_register_script( 'wprss-gallery-js', WPRSS_JS . 'gallery.js', array('jquery'), $version, true );
 
 
 
 
 
 
 
 
79
  }
80
 
81
 
@@ -162,6 +170,13 @@
162
  wp_enqueue_script( 'wprss-admin-help' );
163
  }
164
 
 
 
 
 
 
 
 
165
  if (wprss_is_help_beacon_enabled()) {
166
  wp_enqueue_script('wprss-hs-beacon-js');
167
  wp_enqueue_style('wprss-hs-beacon-css');
76
  ));
77
 
78
  wp_register_script( 'wprss-gallery-js', WPRSS_JS . 'gallery.js', array('jquery'), $version, true );
79
+
80
+ wp_register_script('wpra-tools', WPRSS_JS . 'tools.js', ['jquery'], $version, true);
81
+ wp_register_script('wpra-logs-tool', WPRSS_JS . 'logs-tool.js', ['jquery'], $version, true);
82
+ wp_register_script('wpra-blacklist-tool', WPRSS_JS . 'blacklist-tool.js', ['jquery'], $version, true);
83
+ wp_register_script('wpra-reset-tool', WPRSS_JS . 'reset-tool.js', ['jquery'], $version, true);
84
+ wp_localize_script('wpra-reset-tool', 'WpraResetTool', [
85
+ 'message' => __('Are you sure you want to do this? This operation cannot be undone.', 'wprss')
86
+ ]);
87
  }
88
 
89
 
170
  wp_enqueue_script( 'wprss-admin-help' );
171
  }
172
 
173
+ if ($pageBase === 'wprss_feed_page_wpra_tools') {
174
+ wp_enqueue_script('wpra-tools');
175
+ wp_enqueue_script('wpra-logs-tool');
176
+ wp_enqueue_script('wpra-blacklist-tool');
177
+ wp_enqueue_script('wpra-reset-tool');
178
+ }
179
+
180
  if (wprss_is_help_beacon_enabled()) {
181
  wp_enqueue_script('wprss-hs-beacon-js');
182
  wp_enqueue_style('wprss-hs-beacon-css');
includes/system-info.php CHANGED
@@ -1,58 +1,33 @@
1
  <?php
2
- /**
3
- * System information
4
- *
5
- * @package WPRSSAggregator
6
- * @subpackage Includes
7
- * @since 3.1
8
- * @author Jean Galea <info@wprssaggregator.com>
9
- * @copyright Copyright (c) 2012-2015, Jean Galea. Adapted from Easy Digital Downloads by Pippin Williamson
10
- * @link http://www.wprssaggregator.com/
11
- * @license http://www.gnu.org/licenses/gpl.html
12
- */
13
-
14
- /**
15
- * Generate the system information
16
- *
17
- * @since 3.1
18
- */
19
- function wprss_system_info() {
20
- global $wpdb;
21
-
22
- ?>
23
- <h3><?php _e( 'System Information', WPRSS_TEXT_DOMAIN ) ?></h3>
24
- <?php
25
- $form_url = admin_url( 'edit.php?post_type=wprss_feed&page=wprss-debugging' );
26
- $nonce_url = wp_nonce_url( $form_url, 'wprss-sysinfo' );
27
- ?>
28
- <form action="<?php echo esc_url( $nonce_url ); ?>" method="post">
29
- <textarea readonly="readonly" onclick="this.focus();this.select()" id="system-info-textarea" name="wprss-sysinfo" title="<?php _e( 'To copy the system info, click below then press Ctrl + C (PC) or Cmd + C (Mac).', WPRSS_TEXT_DOMAIN ); ?>"><?php wprss_print_system_info(); ?>
30
- </textarea>
31
- <p class="submit">
32
- <input type="hidden" name="wprss-action" value="download_sysinfo" />
33
- <button type="submit" class="button button-primary" id="wprss-download-sysinfo">
34
- <i class="fa fa-download"></i>
35
- <?php _e( 'Download System Info File', WPRSS_TEXT_DOMAIN ) ?>
36
- </button>
37
- </p>
38
- </form>
39
-
40
- <?php
41
- }
42
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
43
 
44
- /**
45
- * Prints the system information
46
- *
47
- * @since 4.6.8
48
- */
49
- function wprss_print_system_info() {
50
- global $wpdb;
51
 
52
- if ( ! class_exists( 'Browser' ) )
53
- require_once WPRSS_DIR . 'includes/libraries/browser.php';
54
 
55
- $browser = new Browser();
56
 
57
  ?>
58
  ### Begin System Info ###
@@ -156,8 +131,9 @@ foreach ( $plugins as $plugin_path ) {
156
  $plugin_base = plugin_basename( $plugin_path );
157
 
158
  // If the plugin isn't active, don't show it.
159
- if ( ! array_key_exists( $plugin_base, $active_plugins ) )
160
  continue;
 
161
 
162
  $plugin = get_plugin_data( $plugin_path );
163
 
@@ -166,19 +142,17 @@ foreach ( $plugins as $plugin_path ) {
166
 
167
  endif;
168
 
169
- if ( ! is_multisite() ) : ?>
170
 
171
  DEACTIVATED PLUGINS:
172
 
173
  <?php
 
 
 
174
 
175
- foreach ( $inactive_plugins as $inactive_plugin ):
176
-
177
- echo $inactive_plugin['Name']; ?>: <?php echo $inactive_plugin['Version'] ."\n";
178
-
179
- endforeach;
180
-
181
- endif; ?>
182
 
183
  CURRENT THEME:
184
 
@@ -206,7 +180,7 @@ $options = $wpdb->get_results($options_query, OBJECT_K);
206
  $options = apply_filters('wpra/debug/sysinfo/options', $options);
207
 
208
  foreach ($options as $option) {
209
- $unserialized = @unserialize($option->option_value);
210
  $value = apply_filters('wpra/debug/sysinfo/option_value', $unserialized, $option->option_name);
211
 
212
  if ($value === null) {
@@ -246,86 +220,66 @@ foreach ($extensions as $extension) {
246
  }
247
 
248
 
249
- /**
250
- * Generates the System Info Download File
251
- *
252
- * @since 3.1
253
- * @return void
254
- */
255
- function wprss_generate_sysinfo_download() {
256
- nocache_headers();
257
-
258
- check_admin_referer('wprss-sysinfo');
259
-
260
- header( "Content-type: text/plain" );
261
- header( 'Content-Disposition: attachment; filename="wprss-system-info.txt"' );
262
-
263
- echo wp_strip_all_tags( $_POST['wprss-sysinfo'] );
264
- exit;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
265
  }
266
- add_action( 'wprss_download_sysinfo', 'wprss_generate_sysinfo_download' );
267
-
268
-
269
- /**
270
- * Retrieves information about the DB server.
271
- *
272
- * Will use WordPress configuration by default;
273
- * Currently, the following members are present in the result:
274
- * - 'extension': The extension that is used to connect. Possible values: 'mysqli', 'mysql'.
275
- * - 'server_info': The version number of the database engine, i.e. '5.6.22'.
276
- *
277
- * @since 4.7.2
278
- * @param null|string $host The address of the database host, to which to connect.
279
- * May contain the port number in standard URI format.
280
- * Default: value of the DB_HOST constant, if defined, otherwise null.
281
- * @param null|string $username The username to be used for connecting to the databse.
282
- * Default: value of the DB_USER constant, if defined, otherwise null.
283
- * @param null|string $password The password to be used for connecting to the database.
284
- * Default: value of the DB_PASSWORD constant, if defined, otherwise null.
285
- * @param null|int $port An integer, representing the port, at which to connect to the DB server.
286
- * Default: auto-determined from host.
287
- * @return array|null An array, containing the following indexes, if successful: 'extension', 'server_info'.
288
- * Otherwise, null.
289
- */
290
- function wprss_sysinfo_get_db_server( $host = null, $username = null, $password = null, $port = null ) {
291
- $result = array();
292
-
293
- if ( is_null( $host ) && defined( 'DB_HOST') ) $host = DB_HOST;
294
- if ( is_null( $username ) && defined( 'DB_USER') ) $username = DB_USER;
295
- if ( is_null( $password ) && defined( 'DB_PASSWORD') ) $password = DB_PASSWORD;
296
-
297
- $server_address = explode( ':', $host, 2 );
298
- $host = $server_address[0];
299
- $port = is_null( $port )
300
- ? ( isset( $server_address[1] ) ? $server_address[1] : null )
301
- : $port;
302
- $port = $port ? intval( (string)$port ) : null;
303
-
304
- if ( function_exists( 'mysqli_get_server_info' ) ){
305
- $mysqli = new mysqli( $host, $username, $password, '', $port );
306
- $result['extension'] = 'mysqli';
307
- $result['server_info'] = $mysqli->server_info;
308
- return $result;
309
- }
310
 
311
- if ( function_exists( 'mysql_connect' ) ) {
312
- if (version_compare(PHP_VERSION, '7.0', '>=')) {
313
- $result['warning'] = __(
314
- 'The mysql extension is deprecated since PHP 5.5 and removed since PHP 7.0; Use mysqli instead',
315
- 'wprss'
316
- );
317
- $result['extension'] = 'mysql';
318
- $result['server_info'] = '';
319
- return $result;
320
- }
321
-
322
- if ( $port ) $host = implode ( ':', array( $host, $port ) );
323
-
324
- $mysql = mysql_connect( $host, $username, $password );
325
  $result['extension'] = 'mysql';
326
- $result['server_info'] = mysql_get_server_info( $mysql );
327
  return $result;
328
  }
329
 
330
- return null;
 
 
 
 
 
331
  }
 
 
 
1
  <?php
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
+ /**
4
+ * Retrieves the system information.
5
+ *
6
+ * @since 4.17
7
+ *
8
+ * @return string
9
+ */
10
+ function wpra_get_sys_info()
11
+ {
12
+ ob_start();
13
+
14
+ wprss_print_system_info();
15
+
16
+ return ob_get_clean();
17
+ }
18
 
19
+ /**
20
+ * Prints the system information
21
+ *
22
+ * @since 4.6.8
23
+ */
24
+ function wprss_print_system_info() {
25
+ global $wpdb;
26
 
27
+ if ( ! class_exists( 'Browser' ) )
28
+ require_once WPRSS_DIR . 'includes/libraries/browser.php';
29
 
30
+ $browser = new Browser();
31
 
32
  ?>
33
  ### Begin System Info ###
131
  $plugin_base = plugin_basename( $plugin_path );
132
 
133
  // If the plugin isn't active, don't show it.
134
+ if ( !array_key_exists( $plugin_base, $active_plugins ) ) {
135
  continue;
136
+ }
137
 
138
  $plugin = get_plugin_data( $plugin_path );
139
 
142
 
143
  endif;
144
 
145
+ if ( !is_multisite() ) : ?>
146
 
147
  DEACTIVATED PLUGINS:
148
 
149
  <?php
150
+ foreach ( $inactive_plugins as $inactive_plugin ) {
151
+ echo $inactive_plugin['Name']; ?>: <?php echo $inactive_plugin['Version'] . "\n";
152
+ }
153
 
154
+ endif;
155
+ ?>
 
 
 
 
 
156
 
157
  CURRENT THEME:
158
 
180
  $options = apply_filters('wpra/debug/sysinfo/options', $options);
181
 
182
  foreach ($options as $option) {
183
+ $unserialized = maybe_unserialize($option->option_value);
184
  $value = apply_filters('wpra/debug/sysinfo/option_value', $unserialized, $option->option_name);
185
 
186
  if ($value === null) {
220
  }
221
 
222
 
223
+ /**
224
+ * Retrieves information about the DB server.
225
+ *
226
+ * Will use WordPress configuration by default;
227
+ * Currently, the following members are present in the result:
228
+ * - 'extension': The extension that is used to connect. Possible values: 'mysqli', 'mysql'.
229
+ * - 'server_info': The version number of the database engine, i.e. '5.6.22'.
230
+ *
231
+ * @since 4.7.2
232
+ * @param null|string $host The address of the database host, to which to connect.
233
+ * May contain the port number in standard URI format.
234
+ * Default: value of the DB_HOST constant, if defined, otherwise null.
235
+ * @param null|string $username The username to be used for connecting to the databse.
236
+ * Default: value of the DB_USER constant, if defined, otherwise null.
237
+ * @param null|string $password The password to be used for connecting to the database.
238
+ * Default: value of the DB_PASSWORD constant, if defined, otherwise null.
239
+ * @param null|int $port An integer, representing the port, at which to connect to the DB server.
240
+ * Default: auto-determined from host.
241
+ * @return array|null An array, containing the following indexes, if successful: 'extension', 'server_info'.
242
+ * Otherwise, null.
243
+ */
244
+ function wprss_sysinfo_get_db_server( $host = null, $username = null, $password = null, $port = null ) {
245
+ $result = array();
246
+
247
+ if ( is_null( $host ) && defined( 'DB_HOST') ) $host = DB_HOST;
248
+ if ( is_null( $username ) && defined( 'DB_USER') ) $username = DB_USER;
249
+ if ( is_null( $password ) && defined( 'DB_PASSWORD') ) $password = DB_PASSWORD;
250
+
251
+ $server_address = explode( ':', $host, 2 );
252
+ $host = $server_address[0];
253
+ $port = is_null( $port )
254
+ ? ( isset( $server_address[1] ) ? $server_address[1] : null )
255
+ : $port;
256
+ $port = $port ? intval( (string)$port ) : null;
257
+
258
+ if ( function_exists( 'mysqli_get_server_info' ) ){
259
+ $mysqli = new mysqli( $host, $username, $password, '', $port );
260
+ $result['extension'] = 'mysqli';
261
+ $result['server_info'] = $mysqli->server_info;
262
+ return $result;
263
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
264
 
265
+ if ( function_exists( 'mysql_connect' ) ) {
266
+ if (version_compare(PHP_VERSION, '7.0', '>=')) {
267
+ $result['warning'] = __(
268
+ 'The mysql extension is deprecated since PHP 5.5 and removed since PHP 7.0; Use mysqli instead',
269
+ 'wprss'
270
+ );
 
 
 
 
 
 
 
 
271
  $result['extension'] = 'mysql';
272
+ $result['server_info'] = '';
273
  return $result;
274
  }
275
 
276
+ if ( $port ) $host = implode ( ':', array( $host, $port ) );
277
+
278
+ $mysql = mysql_connect( $host, $username, $password );
279
+ $result['extension'] = 'mysql';
280
+ $result['server_info'] = mysql_get_server_info( $mysql );
281
+ return $result;
282
  }
283
+
284
+ return null;
285
+ }
includes/update.php CHANGED
@@ -298,6 +298,9 @@
298
 
299
  // From 4.14.1
300
  'feed_cache_enabled' => 0,
 
 
 
301
  )
302
  );
303
 
298
 
299
  // From 4.14.1
300
  'feed_cache_enabled' => 0,
301
+
302
+ // From 4.17
303
+ 'schedule_future_items' => 0,
304
  )
305
  );
306
 
js/admin-debug.js DELETED
@@ -1,7 +0,0 @@
1
- (function ($) {
2
- $(document).ready(function() {
3
- $('#wprss-error-log-options-link').click(function () {
4
- $('#wprss-error-log-options').slideToggle(200);
5
- });
6
- });
7
- })(jQuery);
 
 
 
 
 
 
 
js/beacon.min.js CHANGED
@@ -1,5 +1,9 @@
1
  !function(e,t,n){function a(){var e=t.getElementsByTagName("script")[0],n=t.createElement("script");n.type="text/javascript",n.async=!0,n.src="https://beacon-v2.helpscout.net",e.parentNode.insertBefore(n,e)}if(e.Beacon=n=function(t,n,a){e.Beacon.readyQueue.push({method:t,options:n,data:a})},n.readyQueue=[],"complete"===t.readyState)return a();e.attachEvent?e.attachEvent("onload",a):e.addEventListener("load",a,!1)}(window,document,window.Beacon||function(){});
 
2
  window.Beacon('init', 'af457a55-16ed-412d-95e9-c84417562092');
3
- window.Beacon('config', {
4
- messagingEnabled: WprssHelpBeaconConfig.premiumSupport
5
- });
 
 
 
1
  !function(e,t,n){function a(){var e=t.getElementsByTagName("script")[0],n=t.createElement("script");n.type="text/javascript",n.async=!0,n.src="https://beacon-v2.helpscout.net",e.parentNode.insertBefore(n,e)}if(e.Beacon=n=function(t,n,a){e.Beacon.readyQueue.push({method:t,options:n,data:a})},n.readyQueue=[],"complete"===t.readyState)return a();e.attachEvent?e.attachEvent("onload",a):e.addEventListener("load",a,!1)}(window,document,window.Beacon||function(){});
2
+
3
  window.Beacon('init', 'af457a55-16ed-412d-95e9-c84417562092');
4
+ // Disable messaging if premium support is not available
5
+ if (!WprssHelpBeaconConfig.premiumSupport) {
6
+ window.Beacon('config', {
7
+ messagingEnabled: false
8
+ });
9
+ }
js/blacklist-tool.js ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ (function ($) {
2
+
3
+ $(document).ready(function () {
4
+ $('button#wpra-add-blacklist-btn').click(function () {
5
+ $('div#wpra-add-blacklist-container').slideToggle(200);
6
+ });
7
+
8
+ var deleteForm = $('#wpra-delete-blacklist-form');
9
+
10
+ $('a.wpra-delete-blacklist-link').click(function () {
11
+ var link = $(this);
12
+ var id = link.data('id');
13
+
14
+ deleteForm.find('#wpra-delete-blacklist-id').val(id);
15
+ deleteForm.submit();
16
+ });
17
+ })
18
+
19
+ })(jQuery);
js/logs-tool.js ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ (function ($) {
2
+
3
+ $(document).ready(function () {
4
+ // Toggle the log options when the "Show options" link is clicked
5
+ $('#wprss-error-log-options-link').click(function (e) {
6
+ $('#wprss-error-log-options').slideToggle(200);
7
+ e.preventDefault();
8
+ });
9
+ });
10
+
11
+ })(jQuery);
js/reset-tool.js ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ (function ($, Config) {
2
+ $(document).ready(function () {
3
+ // Double confirmation for the reset operations
4
+ $('#wpra-delete-items-form, #wpra-reset-settings-form').on('submit', onResetSubmit);
5
+
6
+ function onResetSubmit(e) {
7
+ e.preventDefault();
8
+
9
+ var confirmation = confirm(Config.message);
10
+ if (confirmation == true) {
11
+ // Unhook this function to prevent self-triggering infinite recursion
12
+ $(this).off('submit', onResetSubmit);
13
+
14
+ // Submit the form
15
+ $(this).submit();
16
+
17
+ // Re-attach this function
18
+ $(this).on('submit', onResetSubmit);
19
+
20
+ return false;
21
+ }
22
+ }
23
+ });
24
+ })(jQuery, WpraResetTool);
js/tools.js ADDED
@@ -0,0 +1,198 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ (function ($) {
2
+ var DATA_TOOL = 'wpra-tool';
3
+ var URL_TOOL_PARAM = 'tool';
4
+
5
+ // The tool tabs and pages
6
+ var tabs, pages;
7
+ // Get the current tool from the URL
8
+ var currTool = getUrlParam(window.location, URL_TOOL_PARAM);
9
+
10
+ // When a state is popped, navigate to the corresponding tool
11
+ window.onpopstate = function (event) {
12
+ if (event.state) {
13
+ setCurrentTool(event.state.tool);
14
+ } else {
15
+ setCurrentTool();
16
+ }
17
+ };
18
+
19
+ // Initialize elements and events
20
+ $(document).ready(function () {
21
+ tabs = $('.nav-tab-wrapper > .wpra-tool-tab');
22
+ pages = $('.wpra-tools-container > .wpra-tool');
23
+
24
+ setCurrentTool(currTool);
25
+ pushHistoryTool(currTool, true);
26
+
27
+ // Add click handler for tabs
28
+ tabs.click(onTabClicked);
29
+
30
+ // Initialize links
31
+ pages.find('a').each(function () {
32
+ var el = $(this);
33
+ var href = el.attr('href');
34
+ var tool = getUrlParam(href, URL_TOOL_PARAM);
35
+
36
+ if (!tool) {
37
+ return;
38
+ }
39
+
40
+ // If the link points to a tab, add a click handler for navigation to that tab
41
+ if (rebuildToolUrl(href, '') === rebuildToolUrl(window.location.href, '')) {
42
+ el.click(function (e) {
43
+ navigate(tool);
44
+ e.preventDefault();
45
+ });
46
+ }
47
+ });
48
+ });
49
+
50
+ // Get the tab for a given tool key
51
+ function getTab(key) {
52
+ return tabs.filter(function () {
53
+ return $(this).data('wpra-tool') === key;
54
+ });
55
+ }
56
+
57
+ // Get the page for a given tool key
58
+ function getPage(key) {
59
+ return pages.filter(function () {
60
+ return $(this).data('wpra-tool') === key;
61
+ });
62
+ }
63
+
64
+ // Event handler for when a tab is clicked
65
+ function onTabClicked(e) {
66
+ let target = $(e.target);
67
+ let tool = target.data('wpra-tool');
68
+
69
+ navigate(tool);
70
+ }
71
+
72
+ // Navigates to a particular tool.
73
+ // Preferred over `setCurrentTool()`
74
+ function navigate(tool)
75
+ {
76
+ if (tool === currTool) {
77
+ return;
78
+ }
79
+
80
+ setCurrentTool(tool);
81
+ pushHistoryTool(currTool);
82
+ }
83
+
84
+ // Set the current tool and updates the DOM
85
+ function setCurrentTool(tool)
86
+ {
87
+ showTool(currTool = tool);
88
+ }
89
+
90
+ // Updates the DOM to show a particular tool
91
+ function showTool(tool)
92
+ {
93
+ // Default to first tab
94
+ if (!tool) {
95
+ tool = tabs.first().data('wpra-tool');
96
+ }
97
+
98
+ let tab = getTab(tool);
99
+ let page = getPage(tool);
100
+
101
+ pages.hide();
102
+ tabs.removeClass('nav-tab-active');
103
+
104
+ page.show();
105
+ tab.addClass('nav-tab-active');
106
+ }
107
+
108
+ // Utility function that pushes a tool navigation entry to the browser's history
109
+ function pushHistoryTool(tool, replace) {
110
+ if (!tool) {
111
+ return;
112
+ }
113
+
114
+ var newUrl = rebuildToolUrl(window.location.href, tool);
115
+
116
+ if (replace) {
117
+ history.replaceState({tool: currTool}, window.document.title, newUrl);
118
+ } else {
119
+ history.pushState({tool: currTool}, window.document.title, newUrl);
120
+ }
121
+ }
122
+
123
+ // Utility function that rebuilds a URL for a given tool
124
+ function rebuildToolUrl(url, tool)
125
+ {
126
+ var urlSplit = url.split('?', 2);
127
+ var params = parseQueryString(urlSplit[1]);
128
+ params[URL_TOOL_PARAM] = tool;
129
+ var newParams = stringifyQuery(params);
130
+
131
+ return urlSplit[0] + '?' + newParams;
132
+ }
133
+
134
+ // Utility function to get a URL param
135
+ function getUrlParam(url, name, def) {
136
+ name = name.replace(/[\[\]]/g, '\\$&');
137
+
138
+ var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)');
139
+ var results = regex.exec(url);
140
+
141
+ if (!results) {
142
+ return def;
143
+ }
144
+
145
+ if (!results[2]) {
146
+ return def;
147
+ }
148
+
149
+ return decodeURIComponent(results[2].replace(/\+/g, ' '));
150
+ }
151
+
152
+ function parseQueryString(str) {
153
+ if (typeof str !== 'string') {
154
+ return {};
155
+ }
156
+
157
+ str = str.trim().replace(/^\?/, '');
158
+
159
+ if (!str) {
160
+ return {};
161
+ }
162
+
163
+ return str.trim().split('&').reduce(function (ret, param) {
164
+ var parts = param.replace(/\+/g, ' ').split('=');
165
+ var key = parts[0];
166
+ var val = parts[1];
167
+
168
+ key = decodeURIComponent(key);
169
+ // missing `=` should be `null`:
170
+ // http://w3.org/TR/2012/WD-url-20120524/#collect-url-parameters
171
+ val = val === undefined ? null : decodeURIComponent(val);
172
+
173
+ if (!ret.hasOwnProperty(key)) {
174
+ ret[key] = val;
175
+ } else if (Array.isArray(ret[key])) {
176
+ ret[key].push(val);
177
+ } else {
178
+ ret[key] = [ret[key], val];
179
+ }
180
+
181
+ return ret;
182
+ }, {});
183
+ };
184
+
185
+ function stringifyQuery(obj) {
186
+ return obj ? Object.keys(obj).map(function (key) {
187
+ var val = obj[key];
188
+
189
+ if (Array.isArray(val)) {
190
+ return val.map(function (val2) {
191
+ return encodeURIComponent(key) + '=' + encodeURIComponent(val2);
192
+ }).join('&');
193
+ }
194
+
195
+ return encodeURIComponent(key) + '=' + encodeURIComponent(val);
196
+ }).join('&') : '';
197
+ };
198
+ })(jQuery);
lib/Entities/Properties/AliasProperty.php ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace RebelCode\Entities\Properties;
4
+
5
+ use OutOfBoundsException;
6
+ use RebelCode\Entities\Api\EntityInterface;
7
+ use RebelCode\Entities\Api\PropertyInterface;
8
+
9
+ /**
10
+ * A property implementation that acts as an alias of another property.
11
+ *
12
+ * @since [*next-version*]
13
+ */
14
+ class AliasProperty implements PropertyInterface
15
+ {
16
+ /**
17
+ * @since [*next-version*]
18
+ *
19
+ * @var string
20
+ */
21
+ protected $key;
22
+
23
+ /**
24
+ * Constructor.
25
+ *
26
+ * @since [*next-version*]
27
+ *
28
+ * @param string $key The key of the original property to be aliased.
29
+ */
30
+ public function __construct($key)
31
+ {
32
+ $this->key = $key;
33
+ }
34
+
35
+ /**
36
+ * @inheritdoc
37
+ *
38
+ * @since [*next-version*]
39
+ */
40
+ public function getValue(EntityInterface $entity)
41
+ {
42
+ return $entity->get($this->key);
43
+ }
44
+
45
+ /**
46
+ * @inheritdoc
47
+ *
48
+ * @since [*next-version*]
49
+ */
50
+ public function setValue(EntityInterface $entity, $value)
51
+ {
52
+ return $entity->set([$this->key => $value]);
53
+ }
54
+ }
readme.txt CHANGED
@@ -3,9 +3,9 @@ Contributors: RebelCode, jeangalea, markzahra, Mekku, xedin.unknown
3
  Plugin URI: https://www.wprssaggregator.com
4
  Tags: RSS import, RSS aggregator, feed import, content curation, feed to post
5
  Requires at least: 4.0 or higher
6
- Tested up to: 5.2.2
7
  Requires PHP: 5.4
8
- Stable tag: 4.16
9
  License: GPLv3
10
 
11
  WP RSS Aggregator is the original & most popular WordPress solution for importing RSS feeds, auto-blogging, content curation & aggregation.
@@ -259,13 +259,36 @@ Our complete Knowledge Base with FAQs can be found [here](https://kb.wprssaggreg
259
 
260
  == Changelog ==
261
 
262
- = 4.16 (2019-10-31) =
 
 
 
 
 
 
 
 
263
 
264
  **Changed**
265
- - Overhauled the data set system with a more robust entity system.
266
- - Various database optimizations for better performance.
 
 
 
 
 
 
 
 
267
 
268
  **Fixed**
269
- - Incompatibility with other plugins that use similar import/export mechanisms.
270
- - Incompatibility with PolyLang, causing the block and shortcode to show no feed items.
271
- - Timeout and infinite loop when saving a feed source.
 
 
 
 
 
 
 
3
  Plugin URI: https://www.wprssaggregator.com
4
  Tags: RSS import, RSS aggregator, feed import, content curation, feed to post
5
  Requires at least: 4.0 or higher
6
+ Tested up to: 5.3
7
  Requires PHP: 5.4
8
+ Stable tag: 4.17
9
  License: GPLv3
10
 
11
  WP RSS Aggregator is the original & most popular WordPress solution for importing RSS feeds, auto-blogging, content curation & aggregation.
259
 
260
  == Changelog ==
261
 
262
+ = 4.17 (2019-12-11) =
263
+
264
+ **Added**
265
+ - New "Tools" that replaces the "Blacklist", "Import/Export" and "Debugging" pages.
266
+ - New option to control whether items with future dates are scheduled or published with truncated dates.
267
+ - New "feeds" shortcode parameter to select feed sources by their slug names.
268
+ - New "1 week" update interval option to update feeds once every week.
269
+ - The "Edit Feed Source" page now allows the slug to be edited.
270
+ - The "Edit Feed Source" page now shows shortcode snippets.
271
 
272
  **Changed**
273
+ - RSS feeds that are invalid due to leading whitespace are now trimmed and may now be imported.
274
+ - Images that have the same URL are now downloaded to the media library only once.
275
+ - Updated some styles to match the new WordPress 5.3 aesthetic.
276
+ - Optimized template saving to be more performant and less error prone.
277
+ - Improved error messages in REST API responses.
278
+ - Removed some log messages.
279
+ - Fatal errors are now always logged.
280
+ - Optimized cron-related functionality.
281
+ - The plugin will no longer register cron schedules that already exist.
282
+ - License-related notices are now only shown to users who have access to the Licenses settings page.
283
 
284
  **Fixed**
285
+ - The "Import Source" option did not work.
286
+ - Templates now link imported posts to the local post instead of to the original article.
287
+ - Images with HTML entities in the URL could not be downloaded.
288
+ - Feed items without a PolyLang translation did not show up in templates.
289
+ - PHP notices were triggered when trying to download invalid images.
290
+ - The feed item count in the "Feed Sources" page would show zero when certain add-ons are installed.
291
+ - Removed a warning shown in templates about `reset()` expecting an array.
292
+ - Thumbnails imported by Excerpts & Thumbnails were not shown in templates.
293
+ - Some databases would report the following error during logging: "Column 'date' cannot be null".
294
+ - Unserializing the options for the system info triggered PHP notices.
src/Entities/Collections/FeedBlacklistCollection.php ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace RebelCode\Wpra\Core\Entities\Collections;
4
+
5
+ use RebelCode\Entities\Api\SchemaInterface;
6
+
7
+ /**
8
+ * A collection implementation that is specific to WP RSS Aggregator blacklisted items.
9
+ *
10
+ * @since 4.17
11
+ */
12
+ class FeedBlacklistCollection extends WpEntityCollection
13
+ {
14
+ /**
15
+ * Constructor.
16
+ *
17
+ * @since 4.17
18
+ *
19
+ * @param string $postType The name of the post type.
20
+ * @param SchemaInterface $schema The schema for feed item entities.
21
+ */
22
+ public function __construct($postType, SchemaInterface $schema)
23
+ {
24
+ parent::__construct($postType, $schema);
25
+ }
26
+
27
+ /**
28
+ * {@inheritdoc}
29
+ *
30
+ * @since 4.17
31
+ */
32
+ protected function getBasePostQueryArgs()
33
+ {
34
+ $args = parent::getBasePostQueryArgs();
35
+ $args['post_status'] = 'publish';
36
+
37
+ return $args;
38
+ }
39
+
40
+ /**
41
+ * {@inheritdoc}
42
+ *
43
+ * Overridden to ensure that the status is "publish".
44
+ *
45
+ * @since 4.17
46
+ */
47
+ protected function getNewPostData($data)
48
+ {
49
+ $post = parent::getNewPostData($data);
50
+ $post['post_status'] = 'publish';
51
+
52
+ return $post;
53
+ }
54
+ }
src/Entities/Collections/FeedItemCollection.php CHANGED
@@ -33,6 +33,7 @@ class FeedItemCollection extends WpEntityCollection
33
  {
34
  $args = parent::getBasePostQueryArgs();
35
  $args['post_status'] = 'publish';
 
36
 
37
  return $args;
38
  }
@@ -61,6 +62,19 @@ class FeedItemCollection extends WpEntityCollection
61
  {
62
  $r = parent::handleFilter($queryArgs, $key, $value);
63
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  if ($key === 'sources') {
65
  $queryArgs['meta_query']['relation'] = 'AND';
66
  $queryArgs['meta_query'][] = [
33
  {
34
  $args = parent::getBasePostQueryArgs();
35
  $args['post_status'] = 'publish';
36
+ $args['lang'] = ''; // Disble PolyLang's query filtering
37
 
38
  return $args;
39
  }
62
  {
63
  $r = parent::handleFilter($queryArgs, $key, $value);
64
 
65
+ if ($key === 'feeds') {
66
+ $slugs = $this->_normalizeArray($value);
67
+ $posts = get_posts([
68
+ 'post_name__in' => $slugs,
69
+ 'post_type' => 'wprss_feed'
70
+ ]);
71
+ $ids = array_map(function ($post) {
72
+ return $post->ID;
73
+ }, $posts);
74
+
75
+ return $this->handleFilter($queryArgs, 'sources', $ids);
76
+ }
77
+
78
  if ($key === 'sources') {
79
  $queryArgs['meta_query']['relation'] = 'AND';
80
  $queryArgs['meta_query'][] = [
src/Entities/Collections/WpEntityCollection.php CHANGED
@@ -213,7 +213,7 @@ class WpEntityCollection extends AbstractDataSet implements CollectionInterface
213
  */
214
  public function clear()
215
  {
216
- foreach ($this->getIterator() as $post) {
217
  $this->delete($post->ID);
218
  }
219
  }
@@ -259,6 +259,13 @@ class WpEntityCollection extends AbstractDataSet implements CollectionInterface
259
  $post = $this->get($key);
260
  $data = $this->getUpdatePostData($key, $data);
261
 
 
 
 
 
 
 
 
262
  foreach ($data as $k => $v) {
263
  $post[$k] = $v;
264
  }
213
  */
214
  public function clear()
215
  {
216
+ foreach ($this->doWpQuery() as $post) {
217
  $this->delete($post->ID);
218
  }
219
  }
259
  $post = $this->get($key);
260
  $data = $this->getUpdatePostData($key, $data);
261
 
262
+ // Optimization for entities, that can update their properties in bulk
263
+ if ($post instanceof EntityInterface) {
264
+ $post->set($data);
265
+
266
+ return;
267
+ }
268
+
269
  foreach ($data as $k => $v) {
270
  $post[$k] = $v;
271
  }
src/Entities/Properties/WpFtImageUrlProperty.php CHANGED
@@ -20,16 +20,25 @@ class WpFtImageUrlProperty implements PropertyInterface
20
  */
21
  protected $ftImageIdKey;
22
 
 
 
 
 
 
 
 
23
  /**
24
  * Constructor.
25
  *
26
  * @since 4.16
27
  *
28
  * @param string $ftImageIdKey The data store key where the featured image ID is stored.
 
29
  */
30
- public function __construct($ftImageIdKey)
31
  {
32
  $this->ftImageIdKey = $ftImageIdKey;
 
33
  }
34
 
35
  /**
@@ -39,10 +48,19 @@ class WpFtImageUrlProperty implements PropertyInterface
39
  */
40
  public function getValue(EntityInterface $entity)
41
  {
42
- $ftImageId = $entity->getStore()->get($this->ftImageIdKey);
43
- $ftImageUrl = wp_get_attachment_image_url($ftImageId, '');
 
 
 
 
 
 
 
 
 
44
 
45
- return $ftImageUrl;
46
  }
47
 
48
  /**
20
  */
21
  protected $ftImageIdKey;
22
 
23
+ /**
24
+ * @since 4.17
25
+ *
26
+ * @var string
27
+ */
28
+ protected $metaFallback;
29
+
30
  /**
31
  * Constructor.
32
  *
33
  * @since 4.16
34
  *
35
  * @param string $ftImageIdKey The data store key where the featured image ID is stored.
36
+ * @param string $metaFallback Optional meta key to fallback to.
37
  */
38
+ public function __construct($ftImageIdKey, $metaFallback = '')
39
  {
40
  $this->ftImageIdKey = $ftImageIdKey;
41
+ $this->metaFallback = $metaFallback;
42
  }
43
 
44
  /**
48
  */
49
  public function getValue(EntityInterface $entity)
50
  {
51
+ $store = $entity->getStore();
52
+
53
+ $ftImageId = $store->has($this->ftImageIdKey)
54
+ ? $store->get($this->ftImageIdKey)
55
+ : null;
56
+
57
+ if (empty($ftImageId)) {
58
+ return !empty($this->metaFallback) && $store->has($this->metaFallback)
59
+ ? $store->get($this->metaFallback)
60
+ : null;
61
+ }
62
 
63
+ return wp_get_attachment_image_url($ftImageId, '');
64
  }
65
 
66
  /**
src/Entities/Properties/WpPostPermalinkProperty.php ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace RebelCode\Wpra\Core\Entities\Properties;
4
+
5
+ use RebelCode\Entities\Api\EntityInterface;
6
+ use RebelCode\Entities\Api\PropertyInterface;
7
+
8
+ /**
9
+ * A property for WordPress post permalinks. Read-only.
10
+ *
11
+ * @since [*next-version*]
12
+ */
13
+ class WpPostPermalinkProperty implements PropertyInterface
14
+ {
15
+ /**
16
+ * @since [*next-version*]
17
+ *
18
+ * @var PropertyInterface
19
+ */
20
+ protected $idProp;
21
+
22
+ /**
23
+ * Constructor.
24
+ *
25
+ * @since [*next-version*]
26
+ *
27
+ * @param PropertyInterface $idProp The property for the WP Post instance or ID.
28
+ */
29
+ public function __construct(PropertyInterface $idProp)
30
+ {
31
+ $this->idProp = $idProp;
32
+ }
33
+
34
+ /**
35
+ * @inheritdoc
36
+ *
37
+ * @since [*next-version*]
38
+ */
39
+ public function getValue(EntityInterface $entity)
40
+ {
41
+ return get_post_permalink($this->idProp->getValue($entity));
42
+ }
43
+
44
+ /**
45
+ * @inheritdoc
46
+ *
47
+ * @since [*next-version*]
48
+ */
49
+ public function setValue(EntityInterface $entity, $value)
50
+ {
51
+ return [];
52
+ }
53
+ }
src/Entities/Properties/WpraItemSourceProperty.php ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace RebelCode\Wpra\Core\Entities\Properties;
4
+
5
+ use OutOfBoundsException;
6
+ use RebelCode\Entities\Api\EntityInterface;
7
+ use RebelCode\Entities\Api\PropertyInterface;
8
+
9
+ /**
10
+ * A specialized feed item property implementation that falls back to a source property.
11
+ *
12
+ * This property replicates the get behavior of either another feed item property of a property of the feed source.
13
+ * Which one is replicated depends on the value of a boolean "control" property. When the control property has a value
14
+ * of true, the feed item property is used. If the control property has a value of false, or true but the feed item has
15
+ * no value for the property, the feed source property is used instead. All set operations will be handled using the
16
+ * feed item property.
17
+ *
18
+ * @since [*next-version*]
19
+ */
20
+ class WpraItemSourceProperty implements PropertyInterface
21
+ {
22
+ /**
23
+ * @since [*next-version*]
24
+ *
25
+ * @var PropertyInterface
26
+ */
27
+ protected $itemProp;
28
+
29
+ /**
30
+ * @since [*next-version*]
31
+ *
32
+ * @var PropertyInterface
33
+ */
34
+ protected $sourceProp;
35
+
36
+ /**
37
+ * @since [*next-version*]
38
+ *
39
+ * @var string
40
+ */
41
+ protected $controlProp;
42
+
43
+ /**
44
+ * Constructor.
45
+ *
46
+ * @since [*next-version*]
47
+ *
48
+ * @param PropertyInterface $itemProp The feed item property.
49
+ * @param PropertyInterface $sourceProp The feed source property.
50
+ * @param string $controlProp The control property.
51
+ */
52
+ public function __construct(
53
+ PropertyInterface $itemProp,
54
+ PropertyInterface $sourceProp,
55
+ $controlProp
56
+ ) {
57
+ $this->itemProp = $itemProp;
58
+ $this->sourceProp = $sourceProp;
59
+ $this->controlProp = $controlProp;
60
+ }
61
+
62
+ /**
63
+ * @inheritdoc
64
+ *
65
+ * @since [*next-version*]
66
+ */
67
+ public function getValue(EntityInterface $item)
68
+ {
69
+ $feed = $item->get('source');
70
+
71
+ try {
72
+ $control = $feed->get($this->controlProp);
73
+ $value = $control ? $this->itemProp->getValue($item) : null;
74
+
75
+ if (!empty($value)) {
76
+ return $value;
77
+ }
78
+ } catch (OutOfBoundsException $exception) {
79
+ // Do nothing
80
+ }
81
+
82
+ return $this->sourceProp->getValue($feed);
83
+ }
84
+
85
+ /**
86
+ * @inheritdoc
87
+ *
88
+ * @since [*next-version*]
89
+ */
90
+ public function setValue(EntityInterface $entity, $value)
91
+ {
92
+ return $this->itemProp->setValue($entity, $value);
93
+ }
94
+ }
src/Entities/Properties/WpraPostTypeDependentProperty.php ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace RebelCode\Wpra\Core\Entities\Properties;
4
+
5
+ use RebelCode\Entities\Api\EntityInterface;
6
+ use RebelCode\Entities\Api\PropertyInterface;
7
+ use RebelCode\Entities\Properties\Property;
8
+
9
+ /**
10
+ * A property implementation that switches delegation between two properties based on the post type.
11
+ *
12
+ * On get, the post type of entity is checked using an ID property. If the post type is "wprss_feed_item", the "item"
13
+ * property's value is returned. If the post type is anything else, the "post" property's value is returned.
14
+ *
15
+ * This implementation can be configured to save values to either the "item" or the "post" properties.
16
+ *
17
+ * @since 4.17
18
+ */
19
+ class WpraPostTypeDependentProperty implements PropertyInterface
20
+ {
21
+ /**
22
+ * @since 4.17
23
+ *
24
+ * @var PropertyInterface
25
+ */
26
+ protected $idProp;
27
+
28
+ /**
29
+ * @since 4.17
30
+ *
31
+ * @var PropertyInterface
32
+ */
33
+ protected $itemProp;
34
+
35
+ /**
36
+ * @since 4.17
37
+ *
38
+ * @var PropertyInterface
39
+ */
40
+ protected $postProp;
41
+
42
+ /**
43
+ * @since 4.17
44
+ *
45
+ * @var bool
46
+ */
47
+ protected $setToPost;
48
+
49
+ /**
50
+ * Constructor.
51
+ *
52
+ * @since 4.17
53
+ *
54
+ * @param Property $idProp The property for reading the post ID.
55
+ * @param PropertyInterface $itemProp The feed item property.
56
+ * @param PropertyInterface $postProp The post property.
57
+ * @param bool $setToPost True to set values to the post property, false to set values to the feed item
58
+ * property. Default is false.
59
+ */
60
+ public function __construct(
61
+ Property $idProp,
62
+ PropertyInterface $itemProp,
63
+ PropertyInterface $postProp,
64
+ $setToPost = false
65
+ ) {
66
+ $this->idProp = $idProp;
67
+ $this->itemProp = $itemProp;
68
+ $this->postProp = $postProp;
69
+ $this->setToPost = $setToPost;
70
+ }
71
+
72
+ /**
73
+ * @inheritdoc
74
+ *
75
+ * @since 4.17
76
+ */
77
+ public function getValue(EntityInterface $entity)
78
+ {
79
+ $id = $this->idProp->getValue($entity);
80
+ $type = get_post_type($id);
81
+
82
+ return ($type === 'wprss_feed_item')
83
+ ? $this->itemProp->getValue($entity)
84
+ : $this->postProp->getValue($entity);
85
+ }
86
+
87
+ /**
88
+ * @inheritdoc
89
+ *
90
+ * @since 4.17
91
+ */
92
+ public function setValue(EntityInterface $entity, $value)
93
+ {
94
+ if ($this->setToPost) {
95
+ return $this->postProp->setValue($entity, $value);
96
+ }
97
+
98
+ return $this->itemProp->setValue($entity, $value);
99
+ }
100
+ }
src/Entities/Stores/WpPostStore.php CHANGED
@@ -45,7 +45,7 @@ class WpPostStore implements StoreInterface
45
 
46
  $meta = get_post_meta($this->post['ID'], $key);
47
 
48
- if (count($meta) > 0) {
49
  return reset($meta);
50
  }
51
 
45
 
46
  $meta = get_post_meta($this->post['ID'], $key);
47
 
48
+ if (is_array($meta) && count($meta) > 0) {
49
  return reset($meta);
50
  }
51
 
src/ErrorHandler.php CHANGED
@@ -4,6 +4,7 @@ namespace RebelCode\Wpra\Core;
4
 
5
  use Dhii\I18n\StringTranslatingTrait;
6
  use Exception;
 
7
  use Throwable;
8
 
9
  /**
@@ -24,11 +25,11 @@ class ErrorHandler
24
  * The callback to invoke.
25
  *
26
  * @since 4.14
27
- *
28
  * @var callable
29
  */
30
  protected $callback;
31
-
32
  /*
33
  * The previous exception handler.
34
  *
@@ -98,7 +99,7 @@ class ErrorHandler
98
  return;
99
  }
100
 
101
- if ($this->errorOriginInRootDir($throwable->getFile())) {
102
  $this->handleError($throwable);
103
 
104
  return;
@@ -106,31 +107,59 @@ class ErrorHandler
106
 
107
  // Detect an exception thrown from within the root directory
108
  foreach ($throwable->getTrace() as $trace) {
109
- if ($this->errorOriginInRootDir($trace['file'])) {
110
  $this->handleError($throwable);
111
  }
112
  }
113
  }
114
 
115
- protected function errorOriginInRootDir($path)
 
 
 
 
 
 
 
 
 
116
  {
117
  return stripos($path, $this->rootDir) === 0;
118
  }
119
 
120
  /**
 
 
 
 
121
  * @param Exception|Throwable $throwable
122
  */
123
  protected function handleError($throwable)
124
  {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
  if (defined('REST_REQUEST')) {
126
  wp_send_json_error(['error' => $throwable->getMessage(), 'trace' => $throwable->getTrace()], 500);
127
 
128
  return;
129
  }
130
-
131
  if (is_callable($this->callback)) {
132
  call_user_func_array($this->callback, [$throwable]);
133
-
134
  return;
135
  }
136
 
4
 
5
  use Dhii\I18n\StringTranslatingTrait;
6
  use Exception;
7
+ use Psr\Log\LogLevel;
8
  use Throwable;
9
 
10
  /**
25
  * The callback to invoke.
26
  *
27
  * @since 4.14
28
+ *
29
  * @var callable
30
  */
31
  protected $callback;
32
+
33
  /*
34
  * The previous exception handler.
35
  *
99
  return;
100
  }
101
 
102
+ if ($this->isErrorFromRootDir($throwable->getFile())) {
103
  $this->handleError($throwable);
104
 
105
  return;
107
 
108
  // Detect an exception thrown from within the root directory
109
  foreach ($throwable->getTrace() as $trace) {
110
+ if ($this->isErrorFromRootDir($trace['file'])) {
111
  $this->handleError($throwable);
112
  }
113
  }
114
  }
115
 
116
+ /**
117
+ * Checks if an error path is from the root directory.
118
+ *
119
+ * @since 4.14
120
+ *
121
+ * @param string $path The path of the error.
122
+ *
123
+ * @return bool
124
+ */
125
+ protected function isErrorFromRootDir($path)
126
  {
127
  return stripos($path, $this->rootDir) === 0;
128
  }
129
 
130
  /**
131
+ * Handles errors.
132
+ *
133
+ * @since 4.14
134
+ *
135
  * @param Exception|Throwable $throwable
136
  */
137
  protected function handleError($throwable)
138
  {
139
+ // Attemt to log the error
140
+ try {
141
+ wpra_get_logger()->log(
142
+ LogLevel::ERROR,
143
+ 'Exception: "{msg}", at {file} line {line}',
144
+ [
145
+ 'msg' => $throwable->getMessage(),
146
+ 'file' => $throwable->getFile(),
147
+ 'line' => $throwable->getLine(),
148
+ ]
149
+ );
150
+ } catch (Exception $exception) {
151
+ // Ignore
152
+ }
153
+
154
  if (defined('REST_REQUEST')) {
155
  wp_send_json_error(['error' => $throwable->getMessage(), 'trace' => $throwable->getTrace()], 500);
156
 
157
  return;
158
  }
159
+
160
  if (is_callable($this->callback)) {
161
  call_user_func_array($this->callback, [$throwable]);
162
+
163
  return;
164
  }
165
 
src/Handlers/EchoHandler.php ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace RebelCode\Wpra\Core\Handlers;
4
+
5
+ /**
6
+ * A simple handler that echoes a string.
7
+ *
8
+ * @since 4.17
9
+ */
10
+ class EchoHandler
11
+ {
12
+ /**
13
+ * @since 4.17
14
+ *
15
+ * @var string
16
+ */
17
+ protected $string;
18
+
19
+ /**
20
+ * Constructor.
21
+ *
22
+ * @since 4.17
23
+ *
24
+ * @param string $string The string to output.
25
+ */
26
+ public function __construct($string)
27
+ {
28
+ $this->string = $string;
29
+ }
30
+
31
+ /**
32
+ * @inheritdoc
33
+ *
34
+ * @since 4.17
35
+ */
36
+ public function __invoke()
37
+ {
38
+ echo $this->string;
39
+ }
40
+ }
src/Handlers/FeedBlacklist/SaveBlacklistHandler.php DELETED
@@ -1,70 +0,0 @@
1
- <?php
2
-
3
- namespace RebelCode\Wpra\Core\Handlers\FeedBlacklist;
4
-
5
- use RebelCode\Wpra\Core\Handlers\AbstractSavePostHandler;
6
- use WP_Post;
7
-
8
- /**
9
- * The handler for saving custom feed blacklist posts.
10
- *
11
- * @since 4.13
12
- */
13
- class SaveBlacklistHandler extends AbstractSavePostHandler
14
- {
15
- /**
16
- * {@inheritdoc}
17
- *
18
- * @since 4.13
19
- */
20
- protected function savePost(WP_Post $post, $meta, $autoDraft)
21
- {
22
- // Stop if the post is an auto draft
23
- if ($autoDraft) {
24
- return [];
25
- }
26
-
27
- // Make sure blacklist items are never draft
28
- if ($post->post_status === 'draft') {
29
- wp_update_post([
30
- 'ID' => $post->ID,
31
- 'post_status' => 'publish',
32
- ]);
33
- }
34
-
35
- // Check if the URL is empty
36
- if (empty($meta['wprss_permalink'])) {
37
- return [
38
- __('The blacklist item URL is empty. Please enter the URL to blacklist.', 'wprss')
39
- ];
40
- }
41
-
42
- // Save the the blacklisted permalink
43
- update_post_meta($post->ID, 'wprss_permalink', $meta['wprss_permalink']);
44
-
45
- // Empty titles default to the URL
46
- if (empty($post->post_title)) {
47
- wp_update_post([
48
- 'ID' => $post->ID,
49
- 'post_title' => $meta['wprss_permalink'],
50
- ]);
51
- }
52
-
53
- return [];
54
- }
55
-
56
- /**
57
- * {@inheritdoc}
58
- *
59
- * @since 4.13
60
- */
61
- protected function getMetaSchema(WP_Post $post)
62
- {
63
- return [
64
- 'wprss_permalink' => [
65
- 'filter' => FILTER_SANITIZE_URL,
66
- 'default' => get_post_meta($post->ID, 'wprss_permalink', true)
67
- ],
68
- ];
69
- }
70
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/Handlers/Images/RenderItemsImageColumnHandler.php CHANGED
@@ -51,17 +51,15 @@ class RenderItemsImageColumnHandler
51
  }
52
 
53
  $feedItem = $this->feedItems[$postId];
54
- $ftImageId = $feedItem['ft_image'];
55
 
56
- $url = wp_get_attachment_url($ftImageId, '');
57
-
58
- if (empty($url)) {
59
  return;
60
  }
61
 
62
  printf(
63
  '<div><img src="%1$s" alt="%2$s" title="%2$s" class="wpra-item-ft-image" /></div>',
64
- $url,
65
  __('Feed item image', 'wprss')
66
  );
67
  }
51
  }
52
 
53
  $feedItem = $this->feedItems[$postId];
54
+ $ftImageUrl = $feedItem['ft_image_url'];
55
 
56
+ if (empty($ftImageUrl)) {
 
 
57
  return;
58
  }
59
 
60
  printf(
61
  '<div><img src="%1$s" alt="%2$s" title="%2$s" class="wpra-item-ft-image" /></div>',
62
+ $ftImageUrl,
63
  __('Feed item image', 'wprss')
64
  );
65
  }
src/Handlers/RegisterSubMenuPageHandler.php CHANGED
@@ -32,10 +32,19 @@ class RegisterSubMenuPageHandler
32
  * - menu_label
33
  * - capability
34
  * - callback
 
35
  */
36
  public function __construct($info)
37
  {
38
- $this->info = (array) $info;
 
 
 
 
 
 
 
 
39
  }
40
 
41
  /**
@@ -49,7 +58,8 @@ class RegisterSubMenuPageHandler
49
  $this->info['menu_label'],
50
  $this->info['capability'],
51
  $this->info['slug'],
52
- $this->info['callback']
 
53
  );
54
  }
55
  }
32
  * - menu_label
33
  * - capability
34
  * - callback
35
+ * - position
36
  */
37
  public function __construct($info)
38
  {
39
+ $this->info = wp_parse_args((array) $info, [
40
+ 'parent' => null,
41
+ 'slug' => null,
42
+ 'page_title' => null,
43
+ 'menu_label' => null,
44
+ 'capability' => null,
45
+ 'callback' => null,
46
+ 'position' => null,
47
+ ]);
48
  }
49
 
50
  /**
58
  $this->info['menu_label'],
59
  $this->info['capability'],
60
  $this->info['slug'],
61
+ $this->info['callback'],
62
+ $this->info['position']
63
  );
64
  }
65
  }
src/Handlers/RenderMetaBoxTemplateHandler.php ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace RebelCode\Wpra\Core\Handlers;
4
+
5
+ use Dhii\Output\TemplateInterface;
6
+ use RebelCode\Wpra\Core\Data\Collections\CollectionInterface;
7
+
8
+ /**
9
+ * A generic handler for rendering the contents of a WordPress meta box.
10
+ *
11
+ * @since 4.17
12
+ */
13
+ class RenderMetaBoxTemplateHandler
14
+ {
15
+ /**
16
+ * @since 4.17
17
+ *
18
+ * @var TemplateInterface
19
+ */
20
+ protected $template;
21
+
22
+ /**
23
+ * @since 4.17
24
+ *
25
+ * @var CollectionInterface
26
+ */
27
+ protected $collection;
28
+
29
+ /**
30
+ * @since 4.17
31
+ *
32
+ * @var string
33
+ */
34
+ protected $entityKey;
35
+
36
+ /**
37
+ * Constructor.
38
+ *
39
+ * @since [*next-version*]
40
+ *
41
+ * @param TemplateInterface $template
42
+ * @param CollectionInterface $collection
43
+ * @param string $entityKey
44
+ */
45
+ public function __construct(TemplateInterface $template, CollectionInterface $collection, $entityKey = 'entity')
46
+ {
47
+ $this->template = $template;
48
+ $this->collection = $collection;
49
+ $this->entityKey = $entityKey;
50
+ }
51
+
52
+ /**
53
+ * @inheritdoc
54
+ *
55
+ * @since [*next-version*]
56
+ */
57
+ public function __invoke($post, $args = [])
58
+ {
59
+ $entity = isset($this->collection[$post->ID])
60
+ ? $this->collection[$post->ID]
61
+ : [];
62
+
63
+ echo $this->template->render([
64
+ 'args' => [],
65
+ 'post' => $post,
66
+ $this->entityKey => $entity,
67
+ ]);
68
+ }
69
+ }
src/Logger/WpdbLogger.php CHANGED
@@ -180,7 +180,13 @@ class WpdbLogger extends AbstractLogger implements ClearableLoggerInterface, Log
180
 
181
  // Iterate the columns and retrieve the data to insert for each
182
  foreach ($this->columns as $prop => $col) {
183
- $data[$col] = $this->getLogPropData($prop, $level, $message);
 
 
 
 
 
 
184
  }
185
 
186
  return $data;
180
 
181
  // Iterate the columns and retrieve the data to insert for each
182
  foreach ($this->columns as $prop => $col) {
183
+ $value = $this->getLogPropData($prop, $level, $message);
184
+
185
+ if ($value === null) {
186
+ continue;
187
+ }
188
+
189
+ $data[$col] = $value;
190
  }
191
 
192
  return $data;
src/Modules/BlacklistToolModule.php ADDED
@@ -0,0 +1,221 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace RebelCode\Wpra\Core\Modules;
4
+
5
+ use Psr\Container\ContainerInterface;
6
+ use RebelCode\Wpra\Core\Data\Collections\NullCollection;
7
+ use RebelCode\Wpra\Core\Handlers\EchoHandler;
8
+ use RebelCode\Wpra\Core\Templates\NullTemplate;
9
+ use RebelCode\Wpra\Core\Ui\BlacklistTable;
10
+
11
+ /**
12
+ * The module that adds the "Blacklist" tool to WP RSS Aggregator.
13
+ *
14
+ * @since [*next-version*]
15
+ */
16
+ class BlacklistToolModule implements ModuleInterface
17
+ {
18
+ /**
19
+ * @inheritdoc
20
+ *
21
+ * @since [*next-version*]
22
+ */
23
+ public function getFactories()
24
+ {
25
+ return [
26
+ /*
27
+ * Information about the "Blacklist" tool.
28
+ *
29
+ * @since [*next-version*]
30
+ */
31
+ 'wpra/admin/tools/blacklist/info' => function (ContainerInterface $c) {
32
+ return [
33
+ 'name' => __('Blacklist', 'wprss'),
34
+ 'template' => $c->has('wpra/twig/collection')
35
+ ? $c->get('wpra/twig/collection')['admin/tools/blacklist.twig']
36
+ : new NullTemplate(),
37
+ ];
38
+ },
39
+ /*
40
+ * The context to add to the "Tools" page.
41
+ *
42
+ * @since [*next-version*]
43
+ */
44
+ 'wpra/admin/tools/blacklist/page/context' => function (ContainerInterface $c) {
45
+ ob_start();
46
+ $listTable = $c->get('wpra/admin/tools/blacklist/page/list_table');
47
+ $listTable->prepare_items();
48
+ $listTable->display();
49
+ $content = ob_get_clean();
50
+
51
+ return [
52
+ 'list_table' => $content,
53
+ ];
54
+ },
55
+ /*
56
+ * The list table to show on the "Blacklist" tool page.
57
+ *
58
+ * @since [*next-version*]
59
+ */
60
+ 'wpra/admin/tools/blacklist/page/list_table' => function (ContainerInterface $c) {
61
+ return new BlacklistTable($c->get('wpra/admin/tools/blacklist/collection'));
62
+ },
63
+ /*
64
+ * The notice to show when a URL is being added to the blacklist with an empty URL.
65
+ *
66
+ * @since [*next-version*]
67
+ */
68
+ 'wpra/admin/tools/blacklist/empty_url_notice' => function () {
69
+ return sprintf(
70
+ '<div class="notice notice-error is-dismissable"><p>%s</p></div>',
71
+ __('The blacklist item URL is empty. Please enter the URL to blacklist.', 'wprss')
72
+ );
73
+ },
74
+ /*
75
+ * The notice to show when a URL has been added to the blacklist.
76
+ *
77
+ * @since [*next-version*]
78
+ */
79
+ 'wpra/admin/tools/blacklist/added_notice' => function () {
80
+ return sprintf(
81
+ '<div class="notice notice-success is-dismissable"><p>%s</p></div>',
82
+ __('Added to blacklist.', 'wprss')
83
+ );
84
+ },
85
+ /*
86
+ * The handler that listens to requests for adding URLs to the blacklist.
87
+ *
88
+ * @since [*next-version*]
89
+ */
90
+ 'wpra/admin/tools/blacklist/add_handler' => function (ContainerInterface $c) {
91
+ return function () use ($c) {
92
+ $action = filter_input(INPUT_POST, 'wpra_add_blacklist', FILTER_DEFAULT);
93
+ if (empty($action)) {
94
+ return;
95
+ }
96
+
97
+ check_admin_referer('wpra_add_blacklist', 'wpra_add_blacklist_nonce');
98
+
99
+ $title = filter_input(INPUT_POST, 'wpra_blacklist_title', FILTER_DEFAULT);
100
+ $url = filter_input(INPUT_POST, 'wpra_blacklist_url', FILTER_DEFAULT);
101
+
102
+ // URL cannot be empty
103
+ if (empty($url)) {
104
+ $notice = $c->get('wpra/admin/tools/blacklist/empty_url_notice');
105
+ add_action('admin_notices', new EchoHandler($notice));
106
+
107
+ return;
108
+ }
109
+
110
+ // Empty titles default to the URL
111
+ if (empty($title)) {
112
+ $title = $url;
113
+ }
114
+
115
+ $collection = $c->get('wpra/admin/tools/blacklist/collection');
116
+ $collection[] = [
117
+ 'title' => $title,
118
+ 'url' => $url,
119
+ ];
120
+
121
+ $notice = $c->get('wpra/admin/tools/blacklist/added_notice');
122
+ add_action('admin_notices', new EchoHandler($notice));
123
+ };
124
+ },
125
+ /*
126
+ * The handler that listens to blacklist deletion requests.
127
+ *
128
+ * @since [*next-version*]
129
+ */
130
+ 'wpra/admin/tools/blacklist/delete_handler' => function (ContainerInterface $c) {
131
+ return function () use ($c) {
132
+ $id = filter_input(INPUT_POST, 'wpra_delete_blacklist', FILTER_DEFAULT);
133
+ if (empty($id)) {
134
+ return;
135
+ }
136
+
137
+ check_admin_referer('wpra_delete_blacklist', 'wpra_delete_blacklist_nonce');
138
+
139
+ $collection = $c->get('wpra/admin/tools/blacklist/collection');
140
+ if (isset($collection[$id])) {
141
+ unset($collection[$id]);
142
+ }
143
+ };
144
+ },
145
+ /*
146
+ * The handlerthat listens to bulk blacklist deletion requests.
147
+ *
148
+ * @since [*next-version*]
149
+ */
150
+ 'wpra/admin/tools/blacklist/bulk_delete_handler' => function (ContainerInterface $c) {
151
+ return function () use ($c) {
152
+ $bulkAction = filter_input(INPUT_POST, 'action', FILTER_DEFAULT);
153
+ $bulkAction2 = filter_input(INPUT_POST, 'action2', FILTER_DEFAULT);
154
+
155
+ if ($bulkAction !== 'wpra_bulk_delete_blacklist' && $bulkAction2 !== 'wpra_bulk_delete_blacklist') {
156
+ return;
157
+ }
158
+
159
+ $ids = filter_input(INPUT_POST, 'bulk-delete', FILTER_DEFAULT, FILTER_REQUIRE_ARRAY);
160
+ if (!is_array($ids) || empty($ids)) {
161
+ return;
162
+ }
163
+
164
+ check_admin_referer('bulk-blacklist');
165
+
166
+ $collection = $c->get('wpra/admin/tools/blacklist/collection');
167
+ $collection->filter(['id' => $ids])->clear();
168
+ };
169
+ },
170
+ /*
171
+ * Alias for the blacklist entity collection, if it exists.
172
+ *
173
+ * @since [*next-version*]
174
+ */
175
+ 'wpra/admin/tools/blacklist/collection' => function (ContainerInterface $c) {
176
+ return $c->has('wpra/feeds/blacklist/collection')
177
+ ? $c->get('wpra/feeds/blacklist/collection')
178
+ : new NullCollection();
179
+ }
180
+ ];
181
+ }
182
+
183
+ /**
184
+ * @inheritdoc
185
+ *
186
+ * @since [*next-version*]
187
+ */
188
+ public function getExtensions()
189
+ {
190
+ return [
191
+ /*
192
+ * Registers the "Blacklist" tool.
193
+ *
194
+ * @since [*next-version*]
195
+ */
196
+ 'wpra/admin/tools' => function (ContainerInterface $c, $tools) {
197
+ return $tools + ['blacklist' => $c->get('wpra/admin/tools/blacklist/info')];
198
+ },
199
+ /*
200
+ * Adds the context for the "Blacklist" tool on the "Tools" page.
201
+ *
202
+ * @since [*next-version*]
203
+ */
204
+ 'wpra/admin/tools/page/context' => function (ContainerInterface $c, $ctx) {
205
+ return $ctx + ['blacklist' => $c->get('wpra/admin/tools/blacklist/page/context')];
206
+ },
207
+ ];
208
+ }
209
+
210
+ /**
211
+ * @inheritdoc
212
+ *
213
+ * @since [*next-version*]
214
+ */
215
+ public function run(ContainerInterface $c)
216
+ {
217
+ add_action('admin_init', $c->get('wpra/admin/tools/blacklist/add_handler'));
218
+ add_action('admin_init', $c->get('wpra/admin/tools/blacklist/delete_handler'));
219
+ add_action('admin_init', $c->get('wpra/admin/tools/blacklist/bulk_delete_handler'));
220
+ }
221
+ }
src/Modules/BulkAddToolModule.php ADDED
@@ -0,0 +1,93 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace RebelCode\Wpra\Core\Modules;
4
+
5
+ use Aventura\Wprss\Core\Component\BulkSourceImport;
6
+ use Aventura\Wprss\Core\Model\BulkSourceImport\ServiceProvider;
7
+ use Dhii\Di\WritableContainerInterface;
8
+ use Psr\Container\ContainerInterface;
9
+ use RebelCode\Wpra\Core\Templates\NullTemplate;
10
+
11
+ /**
12
+ * The module that adds the "Bulk Add" tool to WP RSS Aggregator.
13
+ *
14
+ * @since 4.17
15
+ */
16
+ class BulkAddToolModule implements ModuleInterface
17
+ {
18
+ /**
19
+ * @inheritdoc
20
+ *
21
+ * @since 4.17
22
+ */
23
+ public function getFactories()
24
+ {
25
+ return [
26
+ /*
27
+ * Information about the "Bulk Add" tool.
28
+ *
29
+ * @since 4.17
30
+ */
31
+ 'wpra/admin/tools/bulk_add/info' => function (ContainerInterface $c) {
32
+ return [
33
+ 'name' => __('Bulk Add Sources', 'wprss'),
34
+ 'template' => $c->has('wpra/twig/collection')
35
+ ? $c->get('wpra/twig/collection')['admin/tools/bulk_add.twig']
36
+ : new NullTemplate(),
37
+ ];
38
+ },
39
+ /*
40
+ * The handler that listens to the bulk add request and creates the feed sources.
41
+ *
42
+ * @since 4.17
43
+ */
44
+ 'wpra/admin/tools/bulk_add/handler' => function (ContainerInterface $c) {
45
+ return function () {
46
+ $feeds = filter_input(INPUT_POST, 'wpra_bulk_feeds', FILTER_DEFAULT);
47
+ if (empty($feeds)) {
48
+ return;
49
+ }
50
+
51
+ // Check nonce
52
+ check_admin_referer('wpra_bulk_add', 'wpra_bulk_nonce');
53
+
54
+ /* @var $importer BulkSourceImport */
55
+ $importer = wprss_wp_container()->get(WPRSS_SERVICE_ID_PREFIX . 'bulk_source_import');
56
+
57
+ $results = $importer->import($feeds);
58
+ wprss()->getAdminAjaxNotices()->addNotice('bulk_feed_import');
59
+ };
60
+ },
61
+ ];
62
+ }
63
+
64
+ /**
65
+ * @inheritdoc
66
+ *
67
+ * @since 4.17
68
+ */
69
+ public function getExtensions()
70
+ {
71
+ return [
72
+ /*
73
+ * Registers the "Bulk Add" tool.
74
+ *
75
+ * @since 4.17
76
+ */
77
+ 'wpra/admin/tools' => function (ContainerInterface $c, $tools) {
78
+ return $tools + ['bulk_add' => $c->get('wpra/admin/tools/bulk_add/info')];
79
+ },
80
+ ];
81
+ }
82
+
83
+ /**
84
+ * @inheritdoc
85
+ *
86
+ * @since 4.17
87
+ */
88
+ public function run(ContainerInterface $c)
89
+ {
90
+ // Register the Bulk Add handler
91
+ add_action('admin_init', $c->get('wpra/admin/tools/bulk_add/handler'));
92
+ }
93
+ }
src/Modules/FeedBlacklistModule.php CHANGED
@@ -3,10 +3,13 @@
3
  namespace RebelCode\Wpra\Core\Modules;
4
 
5
  use Psr\Container\ContainerInterface;
 
 
 
6
  use RebelCode\Wpra\Core\Handlers\AddCptMetaCapsHandler;
7
- use RebelCode\Wpra\Core\Handlers\FeedBlacklist\SaveBlacklistHandler;
8
  use RebelCode\Wpra\Core\Handlers\NullHandler;
9
  use RebelCode\Wpra\Core\Handlers\RegisterCptHandler;
 
10
 
11
  /**
12
  * The feed blacklist module for WP RSS Aggregator.
@@ -83,7 +86,7 @@ class FeedBlacklistModule implements ModuleInterface
83
  'public' => false,
84
  'exclude_from_search' => true,
85
  'show_ui' => true,
86
- 'show_in_menu' => 'edit.php?post_type=wprss_feed',
87
  'capability_type' => $c->get('wpra/feeds/blacklist/cpt/capability'),
88
  'map_meta_cap' => true,
89
  'supports' => ['title'],
@@ -120,13 +123,51 @@ class FeedBlacklistModule implements ModuleInterface
120
  );
121
  },
122
  /*
123
- * The handler for saving custom feed item blacklist posts.
124
  *
125
- * @since 4.13
126
  */
127
- 'wpra/feeds/blacklist/handlers/save_blacklist_item' => function (ContainerInterface $c) {
128
- return new SaveBlacklistHandler($c->get('wpra/feeds/blacklist/cpt/name'));
129
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  ];
131
  }
132
 
@@ -149,6 +190,5 @@ class FeedBlacklistModule implements ModuleInterface
149
  {
150
  add_action('init', $c->get('wpra/feeds/blacklist/handlers/register_cpt'), 11);
151
  add_action('admin_init', $c->get('wpra/feeds/blacklist/handlers/add_cpt_capabilities'));
152
- add_action('save_post', $c->get('wpra/feeds/blacklist/handlers/save_blacklist_item'));
153
  }
154
  }
3
  namespace RebelCode\Wpra\Core\Modules;
4
 
5
  use Psr\Container\ContainerInterface;
6
+ use RebelCode\Entities\Properties\Property;
7
+ use RebelCode\Entities\Schemas\Schema;
8
+ use RebelCode\Wpra\Core\Entities\Collections\FeedBlacklistCollection;
9
  use RebelCode\Wpra\Core\Handlers\AddCptMetaCapsHandler;
 
10
  use RebelCode\Wpra\Core\Handlers\NullHandler;
11
  use RebelCode\Wpra\Core\Handlers\RegisterCptHandler;
12
+ use RebelCode\Wpra\Core\Ui\BlacklistTable;
13
 
14
  /**
15
  * The feed blacklist module for WP RSS Aggregator.
86
  'public' => false,
87
  'exclude_from_search' => true,
88
  'show_ui' => true,
89
+ 'show_in_menu' => false,
90
  'capability_type' => $c->get('wpra/feeds/blacklist/cpt/capability'),
91
  'map_meta_cap' => true,
92
  'supports' => ['title'],
123
  );
124
  },
125
  /*
126
+ * The properties for blacklist entities.
127
  *
128
+ * @since 4.17
129
  */
130
+ 'wpra/feeds/blacklist/properties' => function () {
131
+ return [
132
+ 'id' => new Property('ID'),
133
+ 'title' => new Property('post_title'),
134
+ 'url' => new Property('wprss_permalink'),
135
+ ];
136
+ },
137
+ /*
138
+ * The default values for blacklist entity properties.
139
+ *
140
+ * @since 4.17
141
+ */
142
+ 'wpra/feeds/blacklist/defaults' => function () {
143
+ return [
144
+ 'id' => null,
145
+ 'title' => '',
146
+ 'url' => '',
147
+ ];
148
+ },
149
+ /*
150
+ * The schema for blacklist entities.
151
+ *
152
+ * @since 4.17
153
+ */
154
+ 'wpra/feeds/blacklist/schema' => function (ContainerInterface $c) {
155
+ return new Schema(
156
+ $c->get('wpra/feeds/blacklist/properties'),
157
+ $c->get('wpra/feeds/blacklist/defaults')
158
+ );
159
+ },
160
+ /*
161
+ * The collection for blacklist entities.
162
+ *
163
+ * @since 4.17
164
+ */
165
+ 'wpra/feeds/blacklist/collection' => function (ContainerInterface $c) {
166
+ return new FeedBlacklistCollection(
167
+ $c->get('wpra/feeds/blacklist/cpt/name'),
168
+ $c->get('wpra/feeds/blacklist/schema')
169
+ );
170
+ },
171
  ];
172
  }
173
 
190
  {
191
  add_action('init', $c->get('wpra/feeds/blacklist/handlers/register_cpt'), 11);
192
  add_action('admin_init', $c->get('wpra/feeds/blacklist/handlers/add_cpt_capabilities'));
 
193
  }
194
  }
src/Modules/FeedItemsModule.php CHANGED
@@ -12,7 +12,9 @@ use RebelCode\Wpra\Core\Entities\Collections\FeedItemCollection;
12
  use RebelCode\Wpra\Core\Entities\Properties\TimestampProperty;
13
  use RebelCode\Wpra\Core\Entities\Properties\WpFtImageUrlProperty;
14
  use RebelCode\Wpra\Core\Entities\Properties\WpPostEntityProperty;
15
- use RebelCode\Wpra\Core\Entities\Properties\WpraSourceDefaultProperty;
 
 
16
  use RebelCode\Wpra\Core\Handlers\AddCptMetaCapsHandler;
17
  use RebelCode\Wpra\Core\Handlers\NullHandler;
18
  use RebelCode\Wpra\Core\Handlers\RegisterCptHandler;
@@ -40,28 +42,44 @@ class FeedItemsModule implements ModuleInterface
40
  'wpra/feeds/items/properties' => function (ContainerInterface $c) {
41
  $sourceSchema = $c->get('wpra/feeds/sources/schema');
42
 
 
 
 
 
 
 
 
 
43
  return [
44
- 'id' => new Property('ID'),
45
  'title' => new Property('post_title'),
46
  'content' => new DefaultingProperty(['post_content', 'post_excerpt']),
47
  'excerpt' => new DefaultingProperty(['post_excerpt', 'post_content']),
48
- 'url' => new Property('wprss_item_permalink'),
49
- 'permalink' => new Property('wprss_item_permalink'),
50
  'enclosure' => new Property('wprss_item_enclosure'),
51
  'author' => new Property('wprss_item_author'),
52
  'date' => new Property('wprss_item_date'),
53
  'timestamp' => new TimestampProperty(new Property('post_date_gmt'), 'Y-m-d H:i:s'),
54
- 'source_id' => new Property('wprss_feed_id'),
55
- 'source_name' => new WpraSourceDefaultProperty('wprss_source_name', 'post_title'),
56
- 'source_url' => new WpraSourceDefaultProperty('wprss_source_url', 'wprss_url'),
57
  'ft_image' => new Property('_thumbnail_id'),
58
- 'ft_image_url' => new WpFtImageUrlProperty('_thumbnail_id'),
59
  'is_using_def_image' => new Property('wprss_item_is_using_def_image'),
60
  'images' => new Property('wprss_images'),
61
  'best_image' => new Property('wprss_best_image'),
62
  'embed_url' => new Property('wprss_item_embed_url'),
63
  'is_yt' => new Property('wprss_item_is_yt'),
64
  'yt_embed_url' => new Property('wprss_item_yt_embed_url'),
 
 
 
 
 
 
 
 
 
 
 
65
  // @todo remove after templates 0.2
66
  'source' => new WpPostEntityProperty('wprss_feed_id', $sourceSchema, function ($schema, $store) {
67
  return new EntityDataSet(new Entity($schema, $store));
12
  use RebelCode\Wpra\Core\Entities\Properties\TimestampProperty;
13
  use RebelCode\Wpra\Core\Entities\Properties\WpFtImageUrlProperty;
14
  use RebelCode\Wpra\Core\Entities\Properties\WpPostEntityProperty;
15
+ use RebelCode\Wpra\Core\Entities\Properties\WpPostPermalinkProperty;
16
+ use RebelCode\Wpra\Core\Entities\Properties\WpraItemSourceProperty;
17
+ use RebelCode\Wpra\Core\Entities\Properties\WpraPostTypeDependentProperty;
18
  use RebelCode\Wpra\Core\Handlers\AddCptMetaCapsHandler;
19
  use RebelCode\Wpra\Core\Handlers\NullHandler;
20
  use RebelCode\Wpra\Core\Handlers\RegisterCptHandler;
42
  'wpra/feeds/items/properties' => function (ContainerInterface $c) {
43
  $sourceSchema = $c->get('wpra/feeds/sources/schema');
44
 
45
+ $idProp = new Property('ID');
46
+
47
+ $urlProp = new WpraPostTypeDependentProperty(
48
+ $idProp,
49
+ new Property('wprss_item_permalink'),
50
+ new WpPostPermalinkProperty($idProp)
51
+ );
52
+
53
  return [
54
+ 'id' => $idProp,
55
  'title' => new Property('post_title'),
56
  'content' => new DefaultingProperty(['post_content', 'post_excerpt']),
57
  'excerpt' => new DefaultingProperty(['post_excerpt', 'post_content']),
58
+ 'url' => $urlProp,
59
+ 'permalink' => $urlProp,
60
  'enclosure' => new Property('wprss_item_enclosure'),
61
  'author' => new Property('wprss_item_author'),
62
  'date' => new Property('wprss_item_date'),
63
  'timestamp' => new TimestampProperty(new Property('post_date_gmt'), 'Y-m-d H:i:s'),
 
 
 
64
  'ft_image' => new Property('_thumbnail_id'),
65
+ 'ft_image_url' => new WpFtImageUrlProperty('_thumbnail_id', 'wprss_item_thumbnail'),
66
  'is_using_def_image' => new Property('wprss_item_is_using_def_image'),
67
  'images' => new Property('wprss_images'),
68
  'best_image' => new Property('wprss_best_image'),
69
  'embed_url' => new Property('wprss_item_embed_url'),
70
  'is_yt' => new Property('wprss_item_is_yt'),
71
  'yt_embed_url' => new Property('wprss_item_yt_embed_url'),
72
+ 'source_id' => new Property('wprss_feed_id'),
73
+ 'source_name' => new WpraItemSourceProperty(
74
+ new Property('wprss_item_source_name'),
75
+ new Property('post_title'),
76
+ 'use_source_info'
77
+ ),
78
+ 'source_url' => new WpraItemSourceProperty(
79
+ new Property('wprss_item_source_url'),
80
+ new Property('wprss_url'),
81
+ 'use_source_info'
82
+ ),
83
  // @todo remove after templates 0.2
84
  'source' => new WpPostEntityProperty('wprss_feed_id', $sourceSchema, function ($schema, $store) {
85
  return new EntityDataSet(new Entity($schema, $store));
src/Modules/FeedSourcesModule.php CHANGED
@@ -3,6 +3,7 @@
3
  namespace RebelCode\Wpra\Core\Modules;
4
 
5
  use Psr\Container\ContainerInterface;
 
6
  use RebelCode\Entities\Properties\Property;
7
  use RebelCode\Entities\Schemas\Schema;
8
  use RebelCode\Wpra\Core\Entities\Collections\FeedSourceCollection;
@@ -14,6 +15,7 @@ use RebelCode\Wpra\Core\Handlers\FeedSources\RenderFeedSourceContentHandler;
14
  use RebelCode\Wpra\Core\Handlers\MultiHandler;
15
  use RebelCode\Wpra\Core\Handlers\NullHandler;
16
  use RebelCode\Wpra\Core\Handlers\RegisterCptHandler;
 
17
  use RebelCode\Wpra\Core\Templates\NullTemplate;
18
  use RebelCode\Wpra\Core\Util\Sanitizers\BoolSanitizer;
19
  use RebelCode\Wpra\Core\Util\Sanitizers\CallbackSanitizer;
@@ -44,6 +46,7 @@ class FeedSourcesModule implements ModuleInterface
44
  // == Basic info ==
45
  'id' => new Property('ID'),
46
  'name' => new Property('post_title'),
 
47
  'active' => new SanitizedProperty(
48
  new Property('wprss_state'),
49
  new CallbackSanitizer(function ($state) {
@@ -56,6 +59,7 @@ class FeedSourcesModule implements ModuleInterface
56
  new Property('wprss_import_source'),
57
  new BoolSanitizer()
58
  ),
 
59
  'import_limit' => new SanitizedProperty(
60
  new Property('wprss_limit'),
61
  new IntSanitizer(0, 0)
@@ -300,6 +304,30 @@ class FeedSourcesModule implements ModuleInterface
300
  $c->get('wpra/feeds/sources/handlers/add_cpt_capabilities'),
301
  ]);
302
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
303
  /*
304
  * The handler that saves meta data for feed sources when saved through the edit page.
305
  *
@@ -334,5 +362,10 @@ class FeedSourcesModule implements ModuleInterface
334
  add_filter('the_content', $c->get('wpra/feeds/sources/handlers/render_content'));
335
  add_action('admin_init', $c->get('wpra/feeds/sources/add_capabilities_handler'));
336
  add_action('save_post', $c->get('wpra/feeds/sources/meta_box/save_handler'), 20, 2);
 
 
 
 
 
337
  }
338
  }
3
  namespace RebelCode\Wpra\Core\Modules;
4
 
5
  use Psr\Container\ContainerInterface;
6
+ use RebelCode\Entities\Properties\AliasProperty;
7
  use RebelCode\Entities\Properties\Property;
8
  use RebelCode\Entities\Schemas\Schema;
9
  use RebelCode\Wpra\Core\Entities\Collections\FeedSourceCollection;
15
  use RebelCode\Wpra\Core\Handlers\MultiHandler;
16
  use RebelCode\Wpra\Core\Handlers\NullHandler;
17
  use RebelCode\Wpra\Core\Handlers\RegisterCptHandler;
18
+ use RebelCode\Wpra\Core\Handlers\RenderMetaBoxTemplateHandler;
19
  use RebelCode\Wpra\Core\Templates\NullTemplate;
20
  use RebelCode\Wpra\Core\Util\Sanitizers\BoolSanitizer;
21
  use RebelCode\Wpra\Core\Util\Sanitizers\CallbackSanitizer;
46
  // == Basic info ==
47
  'id' => new Property('ID'),
48
  'name' => new Property('post_title'),
49
+ 'slug' => new Property('post_name'),
50
  'active' => new SanitizedProperty(
51
  new Property('wprss_state'),
52
  new CallbackSanitizer(function ($state) {
59
  new Property('wprss_import_source'),
60
  new BoolSanitizer()
61
  ),
62
+ 'use_source_info' => new AliasProperty('import_source'),
63
  'import_limit' => new SanitizedProperty(
64
  new Property('wprss_limit'),
65
  new IntSanitizer(0, 0)
304
  $c->get('wpra/feeds/sources/handlers/add_cpt_capabilities'),
305
  ]);
306
  },
307
+ /*
308
+ * The handler that renders the source info meta box on the edit page.
309
+ *
310
+ * @since [*next-version*]
311
+ */
312
+ 'wpra/feeds/sources/meta_boxes/save/renderer' => function (ContainerInterface $c) {
313
+ return new RenderMetaBoxTemplateHandler(
314
+ $c->get('wpra/twig/collection')['admin/feeds/save-meta-box.twig'],
315
+ $c->get('wpra/feeds/sources/collection'),
316
+ 'feed'
317
+ );
318
+ },
319
+ /*
320
+ * The handler that renders the shortcode on the edit page.
321
+ *
322
+ * @since [*next-version*]
323
+ */
324
+ 'wpra/feeds/sources/meta_boxes/shortcode/renderer' => function (ContainerInterface $c) {
325
+ return new RenderMetaBoxTemplateHandler(
326
+ $c->get('wpra/twig/collection')['admin/feeds/shortcode.twig'],
327
+ $c->get('wpra/feeds/sources/collection'),
328
+ 'feed'
329
+ );
330
+ },
331
  /*
332
  * The handler that saves meta data for feed sources when saved through the edit page.
333
  *
362
  add_filter('the_content', $c->get('wpra/feeds/sources/handlers/render_content'));
363
  add_action('admin_init', $c->get('wpra/feeds/sources/add_capabilities_handler'));
364
  add_action('save_post', $c->get('wpra/feeds/sources/meta_box/save_handler'), 20, 2);
365
+
366
+ // Show shortcode under feed title input field on the edit page
367
+ add_action('edit_form_after_title', $c->get('wpra/feeds/sources/meta_boxes/shortcode/renderer'));
368
+ // Show extra options in the save metabox on the edit page
369
+ add_action('post_submitbox_start', $c->get('wpra/feeds/sources/meta_boxes/save/renderer'));
370
  }
371
  }
src/Modules/ImportExportToolsModule.php ADDED
@@ -0,0 +1,177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace RebelCode\Wpra\Core\Modules;
4
+
5
+ use Psr\Container\ContainerInterface;
6
+ use RebelCode\Wpra\Core\Templates\NullTemplate;
7
+
8
+ /**
9
+ * The module that adds the "Import" and "Export" tools to WP RSS Aggregator.
10
+ *
11
+ * @since [*next-version*]
12
+ */
13
+ class ImportExportToolsModule implements ModuleInterface
14
+ {
15
+ /**
16
+ * @inheritdoc
17
+ *
18
+ * @since [*next-version*]
19
+ */
20
+ public function getFactories()
21
+ {
22
+ return [
23
+ /*
24
+ * Information about the "Export" tool.
25
+ *
26
+ * @since [*next-version*]
27
+ */
28
+ 'wpra/admin/tools/export/info' => function (ContainerInterface $c) {
29
+ return [
30
+ 'name' => __('Export', 'wprss'),
31
+ 'template' => $c->has('wpra/twig/collection')
32
+ ? $c->get('wpra/twig/collection')['admin/tools/export.twig']
33
+ : new NullTemplate(),
34
+ ];
35
+ },
36
+ /*
37
+ * Information about the "Import" tool.
38
+ *
39
+ * @since [*next-version*]
40
+ */
41
+ 'wpra/admin/tools/import/info' => function (ContainerInterface $c) {
42
+ return [
43
+ 'name' => __('Import', 'wprss'),
44
+ 'template' => $c->has('wpra/twig/collection')
45
+ ? $c->get('wpra/twig/collection')['admin/tools/import.twig']
46
+ : new NullTemplate(),
47
+ ];
48
+ },
49
+ /*
50
+ * The handler that listens to the export request and creates the export file.
51
+ *
52
+ * @since [*next-version*]
53
+ */
54
+ 'wpra/admin/tools/export/handler' => function (ContainerInterface $c) {
55
+ return function () {
56
+ $export = filter_input(INPUT_POST, 'wpra_export', FILTER_DEFAULT);
57
+ if (empty($export)) {
58
+ return;
59
+ }
60
+
61
+ check_admin_referer('wpra_export_settings', 'wpra_export_settings_nonce');
62
+
63
+ $generalSettings = get_option('wprss_settings_general');
64
+ $fullSettings = ['wprss_settings_general' => $generalSettings];
65
+ $fullSettings = apply_filters('wprss_fields_export', $fullSettings);
66
+
67
+ $exportData = [];
68
+ foreach ($fullSettings as $key => $value) {
69
+ $exportData[$key] = maybe_unserialize($value);
70
+ }
71
+
72
+ $blogName = str_replace(' ', '', get_option('blogname'));
73
+ $fileName = sprintf('%s-%s.json', $blogName, date('m-d-Y'));
74
+ $charset = get_option('blog_charset');
75
+
76
+ header('Content-Description: File Transfer');
77
+ header("Content-Type: text/json; charset=$charset");
78
+ header("Content-Disposition: attachment; filename=$fileName");
79
+ echo json_encode($exportData);
80
+
81
+ exit;
82
+ };
83
+ },
84
+ /*
85
+ * The handler that listens to import upload requests and imports settings from the uploaded file.
86
+ *
87
+ * @since [*next-version*]
88
+ */
89
+ 'wpra/admin/tools/import/handler' => function (ContainerInterface $c) {
90
+ return function () {
91
+ $export = filter_input(INPUT_POST, 'wpra_import', FILTER_DEFAULT);
92
+ if (empty($export)) {
93
+ return;
94
+ }
95
+
96
+ check_admin_referer('wpra_import_settings', 'wpra_import_settings_nonce');
97
+
98
+ $fileInfo = isset($_FILES['wpra_import_file'])
99
+ ? $_FILES['wpra_import_file']
100
+ : ['error' => UPLOAD_ERR_NO_FILE];
101
+
102
+ if ($fileInfo['error'] !== UPLOAD_ERR_OK) {
103
+ switch ($fileInfo['error']) {
104
+ case UPLOAD_ERR_NO_FILE:
105
+ $message = __('No file was uploaded. Please select a file.', 'wprss');
106
+ break;
107
+ case UPLOAD_ERR_INI_SIZE:
108
+ case UPLOAD_ERR_FORM_SIZE:
109
+ $message = __('Upload file is too large.', 'wprss');
110
+ break;
111
+ case UPLOAD_ERR_PARTIAL:
112
+ $message = __('The file was not fully uploaded.', 'wprss');
113
+ break;
114
+ default:
115
+ $message = __('The file upload failed. Please try again.', 'wprss');
116
+ break;
117
+ }
118
+
119
+ wp_die($message, __('Upload error', 'wprss'), ['back_link' => true]);
120
+
121
+ exit;
122
+ }
123
+
124
+ $importFile = $_FILES['wpra_import_file'];
125
+ $rawContents = file_get_contents($importFile['tmp_name']);
126
+ $settings = json_decode($rawContents, true);
127
+
128
+ if ($settings === null) {
129
+ wprss()->getAdminAjaxNotices()->addNotice('settings_import_failed');
130
+ exit;
131
+ }
132
+
133
+ foreach ($settings as $key => $value) {
134
+ update_option($key, $value);
135
+ }
136
+
137
+ wprss()->getAdminAjaxNotices()->addNotice('settings_import_success');
138
+ do_action('wprss_settings_imported');
139
+ };
140
+ },
141
+ ];
142
+ }
143
+
144
+ /**
145
+ * @inheritdoc
146
+ *
147
+ * @since [*next-version*]
148
+ */
149
+ public function getExtensions()
150
+ {
151
+ return [
152
+ /*
153
+ * Registers the "Export" and "Import" tools.
154
+ *
155
+ * @since [*next-version*]
156
+ */
157
+ 'wpra/admin/tools' => function (ContainerInterface $c, $tools) {
158
+ return $tools + [
159
+ 'export' => $c->get('wpra/admin/tools/export/info'),
160
+ 'import' => $c->get('wpra/admin/tools/import/info'),
161
+ ];
162
+ },
163
+ ];
164
+ }
165
+
166
+ /**
167
+ * @inheritdoc
168
+ *
169
+ * @since [*next-version*]
170
+ */
171
+ public function run(ContainerInterface $c)
172
+ {
173
+ // Register the Export and Import handlers
174
+ add_action('admin_init', $c->get('wpra/admin/tools/export/handler'));
175
+ add_action('admin_init', $c->get('wpra/admin/tools/import/handler'));
176
+ }
177
+ }
src/Modules/LoggerModule.php CHANGED
@@ -191,68 +191,6 @@ class LoggerModule implements ModuleInterface
191
  'wpra/logging/trunc_logs_cron/first_run' => function (ContainerInterface $c) {
192
  return time() + DAY_IN_SECONDS;
193
  },
194
- /**
195
- * The URL of the page where the log is found.
196
- *
197
- * @since 4.14
198
- */
199
- 'wpra/logging/page/url' => function () {
200
- return admin_url('edit.php?post_type=wprss_feed&page=wprss-debugging');
201
- },
202
- /**
203
- * The name of the nonce used in the debug page to verify the referer of log-related requests.
204
- *
205
- * @since 4.14
206
- */
207
- 'wpra/logging/page/nonce_name' => function () {
208
- return 'wprss-debug-log';
209
- },
210
- /**
211
- * The handler that renders the log.
212
- *
213
- * @since 4.14
214
- */
215
- 'wpra/logging/handlers/render_log' => function (ContainerInterface $c) {
216
- return new RenderLogHandler(
217
- $c->get('wpra/logging/reader'),
218
- $c->get('wpra/twig/collection')['admin/debug/log.twig'],
219
- $c->get('wpra/core/config'),
220
- $c->get('wpra/logging/page/nonce_name')
221
- );
222
- },
223
- /**
224
- * TThe handler that processes the clear log request.
225
- *
226
- * @since 4.14
227
- */
228
- 'wpra/logging/handlers/clear_log' => function (ContainerInterface $c) {
229
- return new ClearLogHandler(
230
- $c->get('wpra/logging/clearer'),
231
- $c->get('wpra/logging/page/nonce_name')
232
- );
233
- },
234
- /**
235
- * The handler that processes the log download request.
236
- *
237
- * @since 4.14
238
- */
239
- 'wpra/logging/handlers/download_log' => function (ContainerInterface $c) {
240
- return new DownloadLogHandler(
241
- $c->get('wpra/logging/reader'),
242
- $c->get('wpra/logging/page/nonce_name')
243
- );
244
- },
245
- /**
246
- * The handler that processes log option saving request.
247
- *
248
- * @since 4.14
249
- */
250
- 'wpra/logging/handlers/save_options' => function (ContainerInterface $c) {
251
- return new SaveLogOptionsHandler(
252
- $c->get('wpra/core/config'),
253
- $c->get('wpra/logging/page/nonce_name')
254
- );
255
- },
256
  /*
257
  * The handler for the log truncation cron job.
258
  *
@@ -306,26 +244,5 @@ class LoggerModule implements ModuleInterface
306
  {
307
  // Hook in the scheduler for the truncate logs cron job
308
  add_action('init', $c->get('wpra/logging/trunc_logs_cron/scheduler'));
309
-
310
- // Renders the log on the debugging page
311
- add_filter('wprss_debug_operations', function ($operations) use ($c) {
312
- $operations['render-error-log'] = apply_filters(
313
- 'wprss_render_error_log_operation',
314
- [
315
- 'nonce' => null,
316
- 'run' => null,
317
- 'render' => $c->get('wpra/logging/handlers/render_log'),
318
- ]
319
- );
320
-
321
- return $operations;
322
- });
323
-
324
- // Register the clear log handler
325
- add_action('admin_init', $c->get('wpra/logging/handlers/clear_log'));
326
- // Register the download log handler
327
- add_action('admin_init', $c->get('wpra/logging/handlers/download_log'));
328
- // Register the save log options handler
329
- add_action('admin_init', $c->get('wpra/logging/handlers/save_options'));
330
  }
331
  }
191
  'wpra/logging/trunc_logs_cron/first_run' => function (ContainerInterface $c) {
192
  return time() + DAY_IN_SECONDS;
193
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
194
  /*
195
  * The handler for the log truncation cron job.
196
  *
244
  {
245
  // Hook in the scheduler for the truncate logs cron job
246
  add_action('init', $c->get('wpra/logging/trunc_logs_cron/scheduler'));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
247
  }
248
  }
src/Modules/LogsToolModule.php ADDED
@@ -0,0 +1,157 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace RebelCode\Wpra\Core\Modules;
4
+
5
+ use Psr\Container\ContainerInterface;
6
+ use RebelCode\Wpra\Core\Handlers\Logger\ClearLogHandler;
7
+ use RebelCode\Wpra\Core\Handlers\Logger\DownloadLogHandler;
8
+ use RebelCode\Wpra\Core\Handlers\Logger\SaveLogOptionsHandler;
9
+ use RebelCode\Wpra\Core\Templates\NullTemplate;
10
+
11
+ /**
12
+ * The module that adds the "Logs" tool to WP RSS Aggregator.
13
+ *
14
+ * @since 4.17
15
+ */
16
+ class LogsToolModule implements ModuleInterface
17
+ {
18
+ /**
19
+ * @inheritdoc
20
+ *
21
+ * @since 4.17
22
+ */
23
+ public function getFactories()
24
+ {
25
+ return [
26
+ /*
27
+ * Information about the "Logs" tool.
28
+ *
29
+ * @since 4.17
30
+ */
31
+ 'wpra/admin/tools/logs/info' => function (ContainerInterface $c) {
32
+ return [
33
+ 'name' => __('Logs', 'wprss'),
34
+ 'template' => $c->has('wpra/twig/collection')
35
+ ? $c->get('wpra/twig/collection')['admin/tools/logs.twig']
36
+ : new NullTemplate(),
37
+ ];
38
+ },
39
+ /*
40
+ * The number of logs to show in the "logs" tool page.
41
+ *
42
+ * @since 4.17
43
+ */
44
+ 'wpra/admin/tools/logs/num_logs' => function () {
45
+ return 200;
46
+ },
47
+ /*
48
+ * Additional context to add to the "Tools" page.
49
+ *
50
+ * @since 4.17
51
+ */
52
+ 'wpra/admin/tools/logs/context' => function (ContainerInterface $c) {
53
+ $numLogs = $c->get('wpra/admin/tools/logs/num_logs');
54
+ $nonce = $c->get('wpra/admin/tools/logs/page/nonce');
55
+
56
+ $logs = $c->has('wpra/logging/reader')
57
+ ? $c->get('wpra/logging/reader')->getLogs($numLogs)
58
+ : [];
59
+ $config = $c->has('wpra/core/config')
60
+ ? $c->get('wpra/core/config')
61
+ : [];
62
+
63
+ return [
64
+ 'logs' => [
65
+ 'entries' => $logs,
66
+ 'config' => $config,
67
+ 'nonce' => $nonce,
68
+ 'show_options' => true,
69
+ ],
70
+ ];
71
+ },
72
+ /*
73
+ * The name of the nonce used in the "Logs" tool page to verify the referer of log-related requests.
74
+ *
75
+ * @since 4.14
76
+ */
77
+ 'wpra/admin/tools/logs/page/nonce' => function () {
78
+ return 'wpra_debug_log';
79
+ },
80
+ /*
81
+ * The handler that listens to log clear requests and clears the log.
82
+ *
83
+ * @since 4.17
84
+ */
85
+ 'wpra/admin/tools/logs/clear_handler' => function (ContainerInterface $c) {
86
+ return new ClearLogHandler(
87
+ $c->get('wpra/logging/clearer'),
88
+ $c->get('wpra/admin/tools/logs/page/nonce')
89
+ );
90
+ },
91
+ /*
92
+ * The handler that listens to log download requests and sends the log file.
93
+ *
94
+ * @since 4.17
95
+ */
96
+ 'wpra/admin/tools/logs/download_handler' => function (ContainerInterface $c) {
97
+ return new DownloadLogHandler(
98
+ $c->get('wpra/logging/reader'),
99
+ $c->get('wpra/admin/tools/logs/page/nonce')
100
+ );
101
+ },
102
+ /*
103
+ * The handler that listens to log config save requests and saves the logging config.
104
+ *
105
+ * @since 4.17
106
+ */
107
+ 'wpra/admin/tools/logs/save_config_handler' => function (ContainerInterface $c) {
108
+ return new SaveLogOptionsHandler(
109
+ $c->get('wpra/core/config'),
110
+ $c->get('wpra/admin/tools/logs/page/nonce')
111
+ );
112
+ },
113
+ ];
114
+ }
115
+
116
+ /**
117
+ * @inheritdoc
118
+ *
119
+ * @since 4.17
120
+ */
121
+ public function getExtensions()
122
+ {
123
+ return [
124
+ /*
125
+ * Registers the "Logs" tool.
126
+ *
127
+ * @since 4.17
128
+ */
129
+ 'wpra/admin/tools' => function (ContainerInterface $c, $tools) {
130
+ return $tools + ['logs' => $c->get('wpra/admin/tools/logs/info')];
131
+ },
132
+ /*
133
+ * Adds the logs to the render context on the "Tools" page.
134
+ *
135
+ * @since 4.17
136
+ */
137
+ 'wpra/admin/tools/page/context' => function (ContainerInterface $c, $ctx) {
138
+ return $ctx + $c->get('wpra/admin/tools/logs/context');
139
+ },
140
+ ];
141
+ }
142
+
143
+ /**
144
+ * @inheritdoc
145
+ *
146
+ * @since 4.17
147
+ */
148
+ public function run(ContainerInterface $c)
149
+ {
150
+ // Register the clear log handler
151
+ add_action('admin_init', $c->get('wpra/admin/tools/logs/clear_handler'));
152
+ // Register the download log handler
153
+ add_action('admin_init', $c->get('wpra/admin/tools/logs/download_handler'));
154
+ // Register the save log options handler
155
+ add_action('admin_init', $c->get('wpra/admin/tools/logs/save_config_handler'));
156
+ }
157
+ }
src/Modules/ResetToolModule.php ADDED
@@ -0,0 +1,150 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace RebelCode\Wpra\Core\Modules;
4
+
5
+ use Psr\Container\ContainerInterface;
6
+ use RebelCode\Wpra\Core\Templates\NullTemplate;
7
+
8
+ /**
9
+ * The module that adds the "Reset" tool to WP RSS Aggregator.
10
+ *
11
+ * @since [*next-version*]
12
+ */
13
+ class ResetToolModule implements ModuleInterface
14
+ {
15
+ /**
16
+ * @inheritdoc
17
+ *
18
+ * @since [*next-version*]
19
+ */
20
+ public function getFactories()
21
+ {
22
+ return [
23
+ /*
24
+ * Information about the "Reset" tool.
25
+ *
26
+ * @since [*next-version*]
27
+ */
28
+ 'wpra/admin/tools/reset/info' => function (ContainerInterface $c) {
29
+ return [
30
+ 'name' => __('Reset', 'wprss'),
31
+ 'template' => $c->has('wpra/twig/collection')
32
+ ? $c->get('wpra/twig/collection')['admin/tools/reset.twig']
33
+ : new NullTemplate(),
34
+ ];
35
+ },
36
+ /*
37
+ * The handler that listens to items delete requests and deletes the imported items.
38
+ *
39
+ * @since [*next-version*]
40
+ */
41
+ 'wpra/admin/tools/reset/items_handler' => function (ContainerInterface $c) {
42
+ return function () use ($c) {
43
+ $reset = filter_input(INPUT_POST, 'wpra_delete_all_items', FILTER_DEFAULT);
44
+ if (empty($reset)) {
45
+ return;
46
+ }
47
+
48
+ check_admin_referer('wpra_delete_all_items', 'wpra_delete_all_items_nonce');
49
+
50
+ wprss_feed_reset();
51
+
52
+ add_action('admin_notices', function () use ($c) {
53
+ echo $c->get('wpra/admin/tools/reset/items_notice');
54
+ });
55
+ };
56
+ },
57
+ /*
58
+ * The handler that listens to settings reset requests and resets the settings.
59
+ *
60
+ * @since [*next-version*]
61
+ */
62
+ 'wpra/admin/tools/reset/settings_handler' => function (ContainerInterface $c) {
63
+ return function () use ($c) {
64
+ $reset = filter_input(INPUT_POST, 'wpra_reset_settings', FILTER_DEFAULT);
65
+ if (empty($reset)) {
66
+ return;
67
+ }
68
+
69
+ check_admin_referer('wpra_reset_settings', 'wpra_reset_settings_nonce');
70
+ do_action( 'wprss_before_restore_settings' );
71
+
72
+ foreach ($c->get('wpra/admin/tools/reset/settings_to_reset') as $setting ) {
73
+ delete_option($setting);
74
+ }
75
+
76
+ do_action( 'wprss_after_restore_settings' );
77
+ add_action('admin_notices', function () use ($c) {
78
+ echo $c->get('wpra/admin/tools/reset/settings_notice');
79
+ });
80
+ };
81
+ },
82
+ /*
83
+ * The notice to show when the items are being deleted.
84
+ *
85
+ * @since [*next-version*]
86
+ */
87
+ 'wpra/admin/tools/reset/items_notice' => function () {
88
+ return sprintf(
89
+ '<div class="updated"><p>%s</p></div>',
90
+ __('The items are being deleted in the background.', 'wprss')
91
+ );
92
+ },
93
+ /*
94
+ * The notice to show when the settings have been reset.
95
+ *
96
+ * @since [*next-version*]
97
+ */
98
+ 'wpra/admin/tools/reset/settings_notice' => function () {
99
+ return sprintf(
100
+ '<div class="updated"><p>%s</p></div>',
101
+ __('The plugin settings have been reset to default.', 'wprss')
102
+ );
103
+ },
104
+ 'wpra/admin/tools/reset/settings_to_reset' => function () {
105
+ return apply_filters(
106
+ 'wprss_settings_to_restore',
107
+ [
108
+ 'wprss_settings_general',
109
+ 'wprss_settings_notices',
110
+ 'wprss_addon_notices',
111
+ 'wprss_pwsv',
112
+ 'wprss_db_version',
113
+ WPRSS_INTRO_DID_INTRO_OPTION,
114
+ WPRSS_UPDATE_PAGE_PREV_VERSION_OPTION,
115
+ ]
116
+ );
117
+ },
118
+ ];
119
+ }
120
+
121
+ /**
122
+ * @inheritdoc
123
+ *
124
+ * @since [*next-version*]
125
+ */
126
+ public function getExtensions()
127
+ {
128
+ return [
129
+ /*
130
+ * Registers the "Reset" tool.
131
+ *
132
+ * @since [*next-version*]
133
+ */
134
+ 'wpra/admin/tools' => function (ContainerInterface $c, $tools) {
135
+ return $tools + ['reset' => $c->get('wpra/admin/tools/reset/info')];
136
+ },
137
+ ];
138
+ }
139
+
140
+ /**
141
+ * @inheritdoc
142
+ *
143
+ * @since [*next-version*]
144
+ */
145
+ public function run(ContainerInterface $c)
146
+ {
147
+ add_action('admin_init', $c->get('wpra/admin/tools/reset/settings_handler'));
148
+ add_action('admin_init', $c->get('wpra/admin/tools/reset/items_handler'));
149
+ }
150
+ }
src/Modules/SysInfoToolModule.php ADDED
@@ -0,0 +1,98 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace RebelCode\Wpra\Core\Modules;
4
+
5
+ use Psr\Container\ContainerInterface;
6
+ use RebelCode\Wpra\Core\Templates\NullTemplate;
7
+
8
+ /**
9
+ * The module that adds the "System Info" tool to WP RSS Aggregator.
10
+ *
11
+ * @since [*next-version*]
12
+ */
13
+ class SysInfoToolModule implements ModuleInterface
14
+ {
15
+ /**
16
+ * @inheritdoc
17
+ *
18
+ * @since [*next-version*]
19
+ */
20
+ public function getFactories()
21
+ {
22
+ return [
23
+ /*
24
+ * Information about the "System Info" tool.
25
+ *
26
+ * @since [*next-version*]
27
+ */
28
+ 'wpra/admin/tools/sys_info/info' => function (ContainerInterface $c) {
29
+ return [
30
+ 'name' => __('System Info', 'wprss'),
31
+ 'template' => $c->has('wpra/twig/collection')
32
+ ? $c->get('wpra/twig/collection')['admin/tools/sys_info.twig']
33
+ : new NullTemplate(),
34
+ ];
35
+ },
36
+ /*
37
+ * The handler that listens to the system info download request.
38
+ *
39
+ * @since [*next-version*]
40
+ */
41
+ 'wpra/admin/tools/sys_info/dl_handler' => function (ContainerInterface $c) {
42
+ return function () {
43
+ $dlSysInfo = filter_input(INPUT_POST, 'wpra_dl_sysinfo', FILTER_DEFAULT);
44
+ if (empty($dlSysInfo)) {
45
+ return;
46
+ }
47
+
48
+ // Check nonce
49
+ check_admin_referer('wpra_dl_sys_info', 'wpra_dl_sys_info_nonce');
50
+
51
+ nocache_headers();
52
+ header("Content-type: text/plain");
53
+ header('Content-Disposition: attachment; filename="wprss-system-info.txt"');
54
+ echo wp_strip_all_tags(wpra_get_sys_info());
55
+ exit;
56
+ };
57
+ },
58
+ ];
59
+ }
60
+
61
+ /**
62
+ * @inheritdoc
63
+ *
64
+ * @since [*next-version*]
65
+ */
66
+ public function getExtensions()
67
+ {
68
+ return [
69
+ /*
70
+ * Registers the "System Info" tool.
71
+ *
72
+ * @since [*next-version*]
73
+ */
74
+ 'wpra/admin/tools' => function (ContainerInterface $c, $tools) {
75
+ return $tools + ['sys_info' => $c->get('wpra/admin/tools/sys_info/info')];
76
+ },
77
+ /*
78
+ * Adds the system information to the render context on the "Tools" page.
79
+ *
80
+ * @since [*next-version*]
81
+ */
82
+ 'wpra/admin/tools/page/context' => function (ContainerInterface $c, $ctx) {
83
+ return $ctx + ['sys_info' => wpra_get_sys_info()];
84
+ },
85
+ ];
86
+ }
87
+
88
+ /**
89
+ * @inheritdoc
90
+ *
91
+ * @since [*next-version*]
92
+ */
93
+ public function run(ContainerInterface $c)
94
+ {
95
+ // Register the system info download handler
96
+ add_action('admin_init', $c->get('wpra/admin/tools/sys_info/dl_handler'));
97
+ }
98
+ }
src/Modules/ToolsModule.php ADDED
@@ -0,0 +1,140 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace RebelCode\Wpra\Core\Modules;
4
+
5
+ use Psr\Container\ContainerInterface;
6
+ use RebelCode\Wpra\Core\Handlers\RegisterSubMenuPageHandler;
7
+ use RebelCode\Wpra\Core\Templates\NullTemplate;
8
+
9
+ /**
10
+ * The module that adds the "Tools" page to WP RSS Aggregator.
11
+ *
12
+ * @since 4.17
13
+ */
14
+ class ToolsModule implements ModuleInterface
15
+ {
16
+ /**
17
+ * @inheritdoc
18
+ *
19
+ * @since 4.17
20
+ */
21
+ public function getFactories()
22
+ {
23
+ return [
24
+ /*
25
+ * The list of available tools.
26
+ *
27
+ * @since 4.17
28
+ */
29
+ 'wpra/admin/tools' => function () {
30
+ return [];
31
+ },
32
+ /*
33
+ * Information about the "Tools" menu.
34
+ *
35
+ * @since 4.17
36
+ */
37
+ 'wpra/admin/tools/menu' => function () {
38
+ return [
39
+ 'slug' => 'wpra_tools',
40
+ 'parent' => 'edit.php?post_type=wprss_feed',
41
+ 'label' => __('Tools', 'wprss'),
42
+ 'position' => 4,
43
+ ];
44
+ },
45
+ /*
46
+ * Information about the "Tools" page.
47
+ *
48
+ * @since 4.17
49
+ */
50
+ 'wpra/admin/tools/page' => function () {
51
+ return [
52
+ 'title' => __('Tools', 'wprss'),
53
+ 'capability' => 'manage_options',
54
+ ];
55
+ },
56
+ /*
57
+ * The template for the "Tools" page.
58
+ *
59
+ * @since 4.17
60
+ */
61
+ 'wpra/admin/tools/page/template' => function (ContainerInterface $c) {
62
+ if (!$c->has('wpra/twig/collection')) {
63
+ return new NullTemplate();
64
+ }
65
+
66
+ return $c->get('wpra/twig/collection')['admin/tools/main.twig'];
67
+ },
68
+ /*
69
+ * The template context for the "Tools" page.
70
+ *
71
+ * @since 4.17
72
+ */
73
+ 'wpra/admin/tools/page/context' => function (ContainerInterface $c) {
74
+ return [];
75
+ },
76
+ /*
77
+ * The render function for the "Tools" page.
78
+ *
79
+ * @since 4.17
80
+ */
81
+ 'wpra/admin/tools/page/render_fn' => function (ContainerInterface $c) {
82
+ return function () use ($c) {
83
+ $tools = $c->get('wpra/admin/tools');
84
+ $context = $c->get('wpra/admin/tools/page/context');
85
+ $template = $c->get('wpra/admin/tools/page/template');
86
+
87
+ // Add the tools the context, rendering their templates
88
+ foreach ($tools as $key => $tool) {
89
+ $context['tools'][$key] = [
90
+ 'name' => $tool['name'],
91
+ 'html' => $tool['template']->render($context),
92
+ ];
93
+ }
94
+
95
+ echo $template->render($context);
96
+ };
97
+ },
98
+ /*
99
+ * The handler that registers the "Tools" menu.
100
+ *
101
+ * @since 4.17
102
+ */
103
+ 'wpra/admin/tools/menu/register_handler' => function (ContainerInterface $c) {
104
+ $menu = $c->get('wpra/admin/tools/menu');
105
+ $page = $c->get('wpra/admin/tools/page');
106
+ $renderFn = $c->get('wpra/admin/tools/page/render_fn');
107
+
108
+ return new RegisterSubMenuPageHandler([
109
+ 'parent' => $menu['parent'],
110
+ 'slug' => $menu['slug'],
111
+ 'page_title' => $page['title'],
112
+ 'menu_label' => $menu['label'],
113
+ 'capability' => $page['capability'],
114
+ 'callback' => $renderFn,
115
+ 'position' => $menu['position'],
116
+ ]);
117
+ },
118
+ ];
119
+ }
120
+
121
+ /**
122
+ * @inheritdoc
123
+ *
124
+ * @since 4.17
125
+ */
126
+ public function getExtensions()
127
+ {
128
+ return [];
129
+ }
130
+
131
+ /**
132
+ * @inheritdoc
133
+ *
134
+ * @since 4.17
135
+ */
136
+ public function run(ContainerInterface $c)
137
+ {
138
+ add_action('admin_menu', $c->get('wpra/admin/tools/menu/register_handler'));
139
+ }
140
+ }
src/RestApi/EndPoints/AbstractRestApiEndPoint.php CHANGED
@@ -33,13 +33,7 @@ abstract class AbstractRestApiEndPoint
33
  // Handle the request and get the response
34
  $response = $this->handle($request);
35
  } catch (Exception $exception) {
36
- // In the event of an exception, the response is set to be a 500 Internal Server error
37
- $response = new WP_Error('wprss_rest_api_error', $exception->getMessage(), ['status' => 500]);
38
- }
39
-
40
- // If the response is an error or no transformer should be used, return the response "as is"
41
- if ($response instanceof WP_Error) {
42
- return $response;
43
  }
44
 
45
  // Retrieve the data
@@ -73,6 +67,45 @@ abstract class AbstractRestApiEndPoint
73
  return new RecursiveToArrayTransformer();
74
  }
75
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  /**
77
  * Handles the request and provides a response.
78
  *
33
  // Handle the request and get the response
34
  $response = $this->handle($request);
35
  } catch (Exception $exception) {
36
+ return $this->exceptionResponse($exception);
 
 
 
 
 
 
37
  }
38
 
39
  // Retrieve the data
67
  return new RecursiveToArrayTransformer();
68
  }
69
 
70
+ /**
71
+ * Creates an erroneous response from an exception.
72
+ *
73
+ * @since 4.17
74
+ *
75
+ * @param Exception $exception The exception from which to create the response.
76
+ *
77
+ * @return WP_Error The erreneous response.
78
+ */
79
+ protected function exceptionResponse(Exception $exception)
80
+ {
81
+ $message = $exception->getMessage();
82
+ $data = [
83
+ 'status' => 500,
84
+ 'trace' => [],
85
+ ];
86
+
87
+ foreach ($exception->getTrace() as $trace) {
88
+ $file = basename($trace['file']);
89
+ $line = $trace['line'];
90
+ $fn = $trace['function'];
91
+ $args = array_map(function ($arg) {
92
+ if (is_scalar($arg)) {
93
+ return $arg;
94
+ }
95
+
96
+ return is_object($arg)
97
+ ? get_class($arg)
98
+ : gettype($arg);
99
+ }, $trace['args']);
100
+
101
+ $argsStr = implode(', ', $args);
102
+
103
+ $data['trace'][] = sprintf('%s(%s) @ %s:%s', $fn, $argsStr, $file, $line);
104
+ }
105
+
106
+ return new WP_Error('wprss_rest_api_error', $message, $data);
107
+ }
108
+
109
  /**
110
  * Handles the request and provides a response.
111
  *
src/Templates/Feeds/MasterFeedsTemplate.php CHANGED
@@ -14,7 +14,7 @@ use RebelCode\Wpra\Core\Data\Collections\CollectionInterface;
14
  use RebelCode\Wpra\Core\Data\DataSetInterface;
15
  use RebelCode\Wpra\Core\Templates\Feeds\Types\FeedTemplateTypeInterface;
16
  use RebelCode\Wpra\Core\Util\ParseArgsWithSchemaCapableTrait;
17
- use RebelCode\Wpra\Core\Util\SanitizeIdCommaListCapableTrait;
18
  use stdClass;
19
  use Traversable;
20
 
@@ -37,7 +37,7 @@ class MasterFeedsTemplate implements TemplateInterface
37
  use ParseArgsWithSchemaCapableTrait;
38
 
39
  /* @since 4.13 */
40
- use SanitizeIdCommaListCapableTrait;
41
 
42
  /* @since 4.13 */
43
  use NormalizeArrayCapableTrait;
14
  use RebelCode\Wpra\Core\Data\DataSetInterface;
15
  use RebelCode\Wpra\Core\Templates\Feeds\Types\FeedTemplateTypeInterface;
16
  use RebelCode\Wpra\Core\Util\ParseArgsWithSchemaCapableTrait;
17
+ use RebelCode\Wpra\Core\Util\SanitizeCommaListCapableTrait;
18
  use stdClass;
19
  use Traversable;
20
 
37
  use ParseArgsWithSchemaCapableTrait;
38
 
39
  /* @since 4.13 */
40
+ use SanitizeCommaListCapableTrait;
41
 
42
  /* @since 4.13 */
43
  use NormalizeArrayCapableTrait;
src/Templates/Feeds/Types/AbstractWpraFeedTemplateType.php CHANGED
@@ -7,7 +7,7 @@ use RebelCode\Wpra\Core\Data\ArrayDataSet;
7
  use RebelCode\Wpra\Core\Data\Collections\CollectionInterface;
8
  use RebelCode\Wpra\Core\Data\DataSetInterface;
9
  use RebelCode\Wpra\Core\Util\ParseArgsWithSchemaCapableTrait;
10
- use RebelCode\Wpra\Core\Util\SanitizeIdCommaListCapableTrait;
11
 
12
  /**
13
  * Abstract implementation of a standard WP RSS Aggregator feed template type.
@@ -33,7 +33,7 @@ abstract class AbstractWpraFeedTemplateType extends AbstractFeedTemplateType
33
  use ParseArgsWithSchemaCapableTrait;
34
 
35
  /* @since 4.13 */
36
- use SanitizeIdCommaListCapableTrait;
37
 
38
  /**
39
  * The name of the root templates directory.
@@ -181,6 +181,12 @@ abstract class AbstractWpraFeedTemplateType extends AbstractFeedTemplateType
181
  return $this->sanitizeIdCommaList($value);
182
  },
183
  ],
 
 
 
 
 
 
184
  'sources' => [
185
  'key' => 'filters/sources',
186
  'filter' => function ($value, $args) {
7
  use RebelCode\Wpra\Core\Data\Collections\CollectionInterface;
8
  use RebelCode\Wpra\Core\Data\DataSetInterface;
9
  use RebelCode\Wpra\Core\Util\ParseArgsWithSchemaCapableTrait;
10
+ use RebelCode\Wpra\Core\Util\SanitizeCommaListCapableTrait;
11
 
12
  /**
13
  * Abstract implementation of a standard WP RSS Aggregator feed template type.
33
  use ParseArgsWithSchemaCapableTrait;
34
 
35
  /* @since 4.13 */
36
+ use SanitizeCommaListCapableTrait;
37
 
38
  /**
39
  * The name of the root templates directory.
181
  return $this->sanitizeIdCommaList($value);
182
  },
183
  ],
184
+ 'feeds' => [
185
+ 'key' => 'filters/feeds',
186
+ 'filter' => function ($value, $args) {
187
+ return $this->sanitizeCommaList($value);
188
+ },
189
+ ],
190
  'sources' => [
191
  'key' => 'filters/sources',
192
  'filter' => function ($value, $args) {
src/Ui/BlacklistTable.php ADDED
@@ -0,0 +1,204 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace RebelCode\Wpra\Core\Ui;
4
+
5
+ use RebelCode\Entities\Api\EntityInterface;
6
+ use RebelCode\Wpra\Core\Data\Collections\CollectionInterface;
7
+ use WP_List_Table;
8
+
9
+ if (!class_exists('WP_List_Table')) {
10
+ require_once(ABSPATH . 'wp-admin/includes/class-wp-list-table.php');
11
+ }
12
+
13
+ /**
14
+ * A custom list table for WP RSS Aggregator blacklisted items.
15
+ *
16
+ * @since 4.17
17
+ */
18
+ class BlacklistTable extends WP_List_Table
19
+ {
20
+ /**
21
+ * @since 4.17
22
+ *
23
+ * @var CollectionInterface
24
+ */
25
+ protected $collection;
26
+
27
+ /**
28
+ * Constructor.
29
+ *
30
+ * @since 4.17
31
+ *
32
+ * @param CollectionInterface $collection The collection of blacklist items.
33
+ */
34
+ public function __construct(CollectionInterface $collection)
35
+ {
36
+ parent::__construct([
37
+ 'singular' => __('Blacklist', 'wprss'),
38
+ 'plural' => __('Blacklist', 'wprss'),
39
+ 'ajax' => false,
40
+ ]);
41
+
42
+ $this->collection = $collection;
43
+ }
44
+
45
+ /**
46
+ * @inheritdoc
47
+ *
48
+ * @since 4.17
49
+ */
50
+ public function no_items()
51
+ {
52
+ echo __('There are no blacklisted items', 'wprss');
53
+ }
54
+
55
+ /**
56
+ * @inheritdoc
57
+ *
58
+ * @since 4.17
59
+ */
60
+ public function has_items() {
61
+ return $this->collection->getCount() > 0;
62
+ }
63
+
64
+ /**
65
+ * @inheritdoc
66
+ *
67
+ * @since 4.17
68
+ */
69
+ public function get_columns()
70
+ {
71
+ return [
72
+ 'cb' => '<input type="checkbox" />',
73
+ 'title' => __('Name', 'wprss'),
74
+ 'url' => __('URL', 'wprss'),
75
+ ];
76
+ }
77
+
78
+ /**
79
+ * @inheritdoc
80
+ *
81
+ * @since 4.17
82
+ */
83
+ protected function get_sortable_columns()
84
+ {
85
+ return ['title'];
86
+ }
87
+
88
+ /**
89
+ * Retrieves the list of hidden columns.
90
+ *
91
+ * @since 4.17
92
+ *
93
+ * @return string[]
94
+ */
95
+ protected function get_hidden_columns()
96
+ {
97
+ return [];
98
+ }
99
+
100
+ /**
101
+ * @inheritdoc
102
+ *
103
+ * @since 4.17
104
+ */
105
+ protected function column_cb($item)
106
+ {
107
+ return sprintf(
108
+ '<input type="checkbox" name="bulk-delete[]" value="%s" />', $item['id']
109
+ );
110
+ }
111
+
112
+ /**
113
+ * Renders row content for cells under the "title" column.
114
+ *
115
+ * @since 4.17
116
+ *
117
+ * @param EntityInterface $item The blacklist item.
118
+ */
119
+ public function column_title($item)
120
+ {
121
+ $id = $item['id'];
122
+ $title = $item['title'];
123
+
124
+ $editUrl = sprintf(admin_url('post.php?post=%s&action=edit'), $id);
125
+ $title = sprintf('<strong><a href="%s">%s</a></strong>', $editUrl, $title);
126
+
127
+ $actions = [
128
+ 'edit' => sprintf(
129
+ '<a href="%s">%s</a>',
130
+ $editUrl, __('Edit')
131
+ ),
132
+ 'delete' => sprintf(
133
+ '<a href="javascript: void(0)" data-id="%s" class="wpra-delete-blacklist-link">%s</a>',
134
+ $id, __('Delete', 'wprss')
135
+ ),
136
+ ];
137
+
138
+ return $title . $this->row_actions($actions);
139
+ }
140
+
141
+ /**
142
+ * Renders row content for cells under the "url" column.
143
+ *
144
+ * @since 4.17
145
+ *
146
+ * @param EntityInterface $item The blacklist item.
147
+ */
148
+ public function column_url($item)
149
+ {
150
+ return sprintf('<a href="%1$s" target="_blank">%1$s</a>', $item['url']);
151
+ }
152
+
153
+ /**
154
+ * @inheritdoc
155
+ *
156
+ * @since 4.17
157
+ */
158
+ protected function column_default($item, $column)
159
+ {
160
+ return print_r($item, true); // Show the whole array for troubleshooting purposes
161
+ }
162
+
163
+ /**
164
+ * @inheritdoc
165
+ *
166
+ * @since 4.17
167
+ */
168
+ protected function get_bulk_actions()
169
+ {
170
+ $actions = [
171
+ 'wpra_bulk_delete_blacklist' => __('Delete'),
172
+ ];
173
+
174
+ return $actions;
175
+ }
176
+
177
+ /**
178
+ * @inheritdoc
179
+ *
180
+ * @since 4.17
181
+ */
182
+ public function prepare_items()
183
+ {
184
+ $this->_column_headers = [
185
+ $this->get_columns(),
186
+ $this->get_hidden_columns(),
187
+ $this->get_sortable_columns(),
188
+ ];
189
+
190
+ $itemsPerPage = $this->get_items_per_page('wpra_blacklist_per_page', 20);
191
+ $currentPage = $this->get_pagenum();
192
+ $totalItems = $this->collection->getCount();
193
+
194
+ $this->set_pagination_args([
195
+ 'total_items' => $totalItems,
196
+ 'per_page' => $itemsPerPage,
197
+ ]);
198
+
199
+ $this->items = $this->collection->filter([
200
+ 'page' => $currentPage,
201
+ 'num_items' => $itemsPerPage,
202
+ ]);
203
+ }
204
+ }
src/Util/{SanitizeIdCommaListCapableTrait.php → SanitizeCommaListCapableTrait.php} RENAMED
@@ -3,22 +3,22 @@
3
  namespace RebelCode\Wpra\Core\Util;
4
 
5
  /**
6
- * Functionality for sanitizing a comma-separated string list of IDs into an array.
7
  *
8
  * @since 4.13
9
  */
10
- trait SanitizeIdCommaListCapableTrait
11
  {
12
  /**
13
- * Sanitizes a list of IDs.
14
  *
15
- * @since 4.13
16
  *
17
  * @param string|array $value A comma separated string list or an array.
18
  *
19
- * @return array The list of IDs.
20
  */
21
- protected function sanitizeIdCommaList($value)
22
  {
23
  if (empty($value)) {
24
  return [];
@@ -29,9 +29,23 @@ trait SanitizeIdCommaListCapableTrait
29
  : explode(',', strval($value));
30
 
31
  $ids = array_map(function ($part) {
32
- return intval(trim($part));
33
  }, $array);
34
 
35
  return array_filter($ids);
36
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37
  }
3
  namespace RebelCode\Wpra\Core\Util;
4
 
5
  /**
6
+ * Functionality for sanitizing a comma-separated string list into an array.
7
  *
8
  * @since 4.13
9
  */
10
+ trait SanitizeCommaListCapableTrait
11
  {
12
  /**
13
+ * Sanitizes a list of strings.
14
  *
15
+ * @since 4.17
16
  *
17
  * @param string|array $value A comma separated string list or an array.
18
  *
19
+ * @return array The list of strings.
20
  */
21
+ protected function sanitizeCommaList($value)
22
  {
23
  if (empty($value)) {
24
  return [];
29
  : explode(',', strval($value));
30
 
31
  $ids = array_map(function ($part) {
32
+ return trim($part);
33
  }, $array);
34
 
35
  return array_filter($ids);
36
  }
37
+
38
+ /**
39
+ * Sanitizes a list of IDs.
40
+ *
41
+ * @since 4.13
42
+ *
43
+ * @param string|array $value A comma separated string list or an array.
44
+ *
45
+ * @return array The list of IDs.
46
+ */
47
+ protected function sanitizeIdCommaList($value)
48
+ {
49
+ return array_map('intval', $this->sanitizeCommaList($value));
50
+ }
51
  }
templates/admin/debug/log.twig DELETED
@@ -1,121 +0,0 @@
1
- <h3>
2
- {% trans "Debug Log" %}
3
-
4
- {% if show_options %}
5
- <small id="wpra-log-options-link-wrap">
6
- <a id="wprss-error-log-options-link" href="javascript:void(0)">
7
- {% trans "Options" %}
8
- </a>
9
- </small>
10
- {% endif %}
11
- </h3>
12
-
13
- {% set numLogs = logs|length %}
14
-
15
- <div class="wpra-log">
16
-
17
- {% if show_options %}
18
- <div id="wprss-error-log-options">
19
- <form id="wprss-log-options-form" method="POST">
20
- {{ wp_nonce_field(nonce_name) }}
21
- <label>
22
- <span>{% trans "Enable logging" %}</span>
23
- <input type="checkbox" name="logging_enabled"
24
- value="1" {% if config['logging/enabled'] %} checked {% endif %} />
25
- </label>
26
-
27
- <br/>
28
-
29
- <label>
30
- <span>{% trans "Auto delete logs older than" %}</span>
31
- <input type="number" name="logging_limit_days" value="{{ config['logging/limit_days'] }}" min="0"/>
32
- <span>{% trans "days" %}</span>
33
- <i>{% trans "(Once per day, logs older than the set amount of days are deleted)" %}</i>
34
- </label>
35
-
36
- <hr/>
37
-
38
- <div id="wpra-log-options-submit-wrap">
39
- <button type="submit" name="wpra-log-options" class="button button-primary" value="1">
40
- {% trans "Save Log Options" %}
41
- </button>
42
- </div>
43
- </form>
44
- </div>
45
- {% endif %}
46
-
47
- {# logging disabled message #}
48
- {% if not config['logging/enabled'] %}
49
- <section class="notice notice-warning notice-inline wpra-log-disabled">
50
- <p>{% trans "Logging is disabled" %}</p>
51
- </section>
52
- {% endif %}
53
-
54
- {# log is empty message #}
55
- {% if numLogs == 0 %}
56
- <section class="notice notice-success notice-inline wpra-empty-log-notice">
57
- <p>{% trans "The log is empty" %}</p>
58
- </section>
59
- {% else %}
60
-
61
- {% if config['logging/enabled'] %}
62
- <p>
63
- <i>{{ "Showing the most recent %d log messages."|trans|format(numLogs) }}</i>
64
- </p>
65
- {% endif %}
66
-
67
- <p>
68
- <strong>{% trans "Filters:" %}</strong>
69
-
70
- <span class="wpra-toggle-logs" data-level="all">
71
- <a href="#">{% trans "All" %}</a>
72
- </span>
73
- <span class="wpra-toggle-logs wpra-selected" data-level="error">
74
- <a href="#">{% trans "Errors" %}</a>
75
- </span>
76
- <span class="wpra-toggle-logs wpra-selected" data-level="info">
77
- <a href="#">{% trans "Info" %}</a>
78
- </span>
79
- <span class="wpra-toggle-logs" data-level="notice">
80
- <a href="#">{% trans "Notice" %}</a>
81
- </span>
82
- <span class="wpra-toggle-logs" data-level="warning">
83
- <a href="#">{% trans "Warnings" %}</a>
84
- </span>
85
- <span class="wpra-toggle-logs" data-level="debug">
86
- <a href="#">{% trans "Debug" %}</a>
87
- </span>
88
- </p>
89
- <div class="wpra-log-container">
90
- <table>
91
- <tbody>
92
- {% for log in logs %}
93
- <tr class="{{ "wpra-log-" ~ log.level }}">
94
- <td class="wpra-log-date">{{ log.date }}</td>
95
- <td class="wpra-log-level">{{ log.level|title }}</td>
96
- <td class="wpra-log-feed">{{ log.feed.post_title }}</td>
97
- <td class="wpra-log-message">{{ log.message }}</td>
98
- </tr>
99
- {% endfor %}
100
- </tbody>
101
- </table>
102
- </div>
103
-
104
- <form id="wprss-clear-error-log-form" method="POST" class="wprss-error-log-action">
105
- {{ wp_nonce_field(nonce_name) }}
106
- <button type="submit" name="wpra-clear-log" class="button button-red" value="1">
107
- <i class="fa fa-trash-o"></i>
108
- {% trans "Clear log" %}
109
- </button>
110
- </form>
111
-
112
- <form id="wprss-download-error-log-form" method="POST" class="wprss-error-log-action">
113
- {{ wp_nonce_field(nonce_name) }}
114
- <button type="submit" name="wpra-download-log" class="button button-primary" value="1">
115
- <i class="fa fa-download"></i>
116
- {% trans "Download log" %}
117
- </button>
118
- </form>
119
-
120
- {% endif %}
121
- </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
templates/admin/feeds/save-meta-box.twig ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="wpra-feed-save-meta-box">
2
+ <label>
3
+ <span>
4
+ {% trans "Slug:" %}
5
+ </span>
6
+ <input type="text"
7
+ name="wpra_feed[slug]"
8
+ value="{{ feed.slug|e("html_attr") }}"
9
+ placeholder="{% trans "Auto Generate" %}"
10
+ />
11
+ </label>
12
+ </div>
templates/admin/feeds/shortcode.twig ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {% if not feed.slug == "" %}
2
+ <div>
3
+ <p class="wpra-feed-shortcodes">
4
+ <strong>{% trans "Shortcode:" %}</strong>
5
+ <code>[wp-rss-aggregator feeds="{{ feed.slug }}"]</code>
6
+
7
+ <span>or</span>
8
+ <code>[wp-rss-aggregator sources="{{ feed.id }}"]</code>
9
+ </p>
10
+ </div>
11
+ <style type="text/css">
12
+ p.wpra-feed-shortcodes {
13
+ user-select: none;
14
+ }
15
+ p.wpra-feed-shortcodes code {
16
+ user-select: text;
17
+ }
18
+ </style>
19
+ {% endif %}
templates/admin/tools/blacklist.twig ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <h3 class="wpra-inline-header">
2
+ {% trans "Blacklist" %}
3
+ </h3>
4
+
5
+ <button class="button wpra-header-button" id="wpra-add-blacklist-btn">
6
+ <i class="fa fa-plus"></i>
7
+ {% trans "Blacklist a URL" %}
8
+ </button>
9
+
10
+ <p class="wpra-no-margin">
11
+ {% trans "The blacklist is a list of feed item URLs. Any items with a URL from this list will be not be imported." %}
12
+ </p>
13
+
14
+ {# Add blacklist form #}
15
+ <div class="wpra-slide-box" id="wpra-add-blacklist-container">
16
+ <form method="post">
17
+ {{ wp_nonce_field('wpra_add_blacklist', 'wpra_add_blacklist_nonce') }}
18
+ <h4>{% trans "Add to Blacklist" %}</h4>
19
+ <input type="hidden" name="wpra_add_blacklist" value="1" />
20
+ <label>
21
+ <span>{% trans "Give it a name" %}</span>
22
+ <div>
23
+ <input type="text" name="wpra_blacklist_title" />
24
+ </div>
25
+ </label>
26
+ <label>
27
+ <span>{% trans "URL to blacklist" %}</span>
28
+ <input type="text" name="wpra_blacklist_url" />
29
+ </label>
30
+
31
+ <div class="wpra-submit-wrap">
32
+ <button class="button button-primary" type="submit">{% trans "Add to blacklist" %}</button>
33
+ </div>
34
+ </form>
35
+ </div>
36
+
37
+ {# Blacklist table #}
38
+ <form method="post">
39
+ <input type="hidden" name="wpra_blacklist_bulk" value="1" />
40
+ {{ blacklist.list_table|raw }}
41
+ </form>
42
+
43
+ {# Delete blacklist form #}
44
+ <form method="POST" id="wpra-delete-blacklist-form">
45
+ <input id="wpra-delete-blacklist-id" type="hidden" name="wpra_delete_blacklist" value="" />
46
+ {{ wp_nonce_field('wpra_delete_blacklist', 'wpra_delete_blacklist_nonce') }}
47
+ </form>
templates/admin/tools/bulk_add.twig ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <h2>{% trans "Bulk Add Sources" %}</h2>
2
+ <p>{% trans "Create multiple feed sources at once, by entering the name and URLs of your feeds below." %}</p>
3
+ <p>{% trans "The feed sources will be created using your global settings." %}</p>
4
+ <p>
5
+ {% trans "Put each feed on its own line, and separate the name and the URL using a comma. For example:" %}
6
+ <code>{% trans "Feed Name, http://www.myfeed.com" %}</code>
7
+ </p>
8
+
9
+ <form id="bulk-add-form" method="POST">
10
+ {{ wp_nonce_field('wpra_bulk_add', 'wpra_bulk_nonce') }}
11
+ <textarea rows="6" cols="80" form="bulk-add-form" name="wpra_bulk_feeds" autofocus></textarea>
12
+
13
+ <p>
14
+ <input type="submit" class="button-secondary" name="wpra_bulk_add" value="{{ "Add Feeds"|trans|e('html_attr') }}" />
15
+ </p>
16
+ </form>
templates/admin/tools/export.twig ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <h3>{% trans "Export Settings" %}</h3>
2
+
3
+ <p>{% trans "Create a file that contains all of your settings for WP RSS Aggregator and its add-ons." %}</p>
4
+
5
+ <form method="post">
6
+ <p class="submit">
7
+ {{ wp_nonce_field('wpra_export_settings', 'wpra_export_settings_nonce') }}
8
+
9
+ <input type="hidden" name="wpra_export" value="1" />
10
+
11
+ <button type="submit" class="button button-secondary">
12
+ <i class="fa fa-download"></i>
13
+ {% trans "Export Settings" %}
14
+ </button>
15
+ </p>
16
+ </form>
templates/admin/tools/import.twig ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <h3>{% trans "Import Settings" %}</h3>
2
+ <p>{% trans "Upload an exported settings file to import your settings for WP RSS Aggregator and its add-ons" %}</p>
3
+
4
+ <form method='post' enctype='multipart/form-data'>
5
+ <p class="submit">
6
+ {{ wp_nonce_field('wpra_import_settings', 'wpra_import_settings_nonce') }}
7
+
8
+ <input type="file" name="wpra_import_file" />
9
+ <input type="hidden" name="wpra_import" value="1" />
10
+
11
+ <button type="submit" class="button button-secondary">
12
+ <i class="fa fa-upload"></i>
13
+ {% trans "Import Settings" %}
14
+ </button>
15
+ </p>
16
+ </form>
templates/admin/tools/logs.twig ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <h3 class="wpra-inline-header">
2
+ {% trans "Debug Log" %}
3
+ </h3>
4
+
5
+ {% set numLogs = logs.entries|length %}
6
+
7
+ {% if numLogs > 0 %}
8
+ <form id="wprss-download-error-log-form" class="wpra-header-form" method="POST">
9
+ {{ wp_nonce_field(logs.nonce) }}
10
+ <button type="submit" name="wpra-download-log" class="button button button-primary wpra-header-button"
11
+ value="1">
12
+ <i class="fa fa-download"></i>
13
+ {% trans "Download" %}
14
+ </button>
15
+ </form>
16
+ {% endif %}
17
+
18
+ <div class="wpra-log">
19
+
20
+ {# logging disabled message #}
21
+ {% if not logs.config['logging/enabled'] %}
22
+ <section class="notice notice-warning notice-inline wpra-log-disabled">
23
+ <p>{% trans "Logging is disabled" %}</p>
24
+ </section>
25
+ {% endif %}
26
+
27
+ {# log is empty message #}
28
+ {% if numLogs == 0 %}
29
+ <section class="notice notice-success notice-inline wpra-empty-log-notice">
30
+ <p>{% trans "The log is empty" %}</p>
31
+ </section>
32
+ {% else %}
33
+
34
+ {% if logs.config['logging/enabled'] %}
35
+ <p class="wpra-logs-shown-msg">
36
+ <i>{{ "Showing the most recent %d log messages."|trans|format(numLogs) }}</i>
37
+ </p>
38
+ {% endif %}
39
+
40
+ <div class="wpra-log-filters">
41
+ <strong>{% trans "Filters:" %}</strong>
42
+
43
+ <span class="wpra-toggle-logs" data-level="all">
44
+ <a href="javascript: void(0)">{% trans "All" %}</a>
45
+ </span>
46
+ <span class="wpra-toggle-logs wpra-selected" data-level="error">
47
+ <a href="javascript: void(0)">{% trans "Errors" %}</a>
48
+ </span>
49
+ <span class="wpra-toggle-logs wpra-selected" data-level="info">
50
+ <a href="javascript: void(0)">{% trans "Info" %}</a>
51
+ </span>
52
+ <span class="wpra-toggle-logs" data-level="notice">
53
+ <a href="javascript: void(0)">{% trans "Notice" %}</a>
54
+ </span>
55
+ <span class="wpra-toggle-logs" data-level="warning">
56
+ <a href="javascript: void(0)">{% trans "Warnings" %}</a>
57
+ </span>
58
+ <span class="wpra-toggle-logs" data-level="debug">
59
+ <a href="javascript: void(0)">{% trans "Debug" %}</a>
60
+ </span>
61
+
62
+ <form id="wprss-clear-error-log-form" method="POST">
63
+ {{ wp_nonce_field(logs.nonce) }}
64
+ <button type="submit" name="wpra-clear-log" class="button button-red" value="1">
65
+ <i class="fa fa-trash-o"></i>
66
+ {% trans "Clear log" %}
67
+ </button>
68
+ </form>
69
+ </div>
70
+
71
+
72
+ <div class="wpra-log-container">
73
+ <table>
74
+ <tbody>
75
+ {% for log in logs.entries %}
76
+ <tr class="{{ "wpra-log-" ~ log.level }}">
77
+ <td class="wpra-log-date">{{ log.date }}</td>
78
+ <td class="wpra-log-level">{{ log.level|title }}</td>
79
+ <td class="wpra-log-feed">{{ log.feed.post_title }}</td>
80
+ <td class="wpra-log-message">{{ log.message }}</td>
81
+ </tr>
82
+ {% endfor %}
83
+ </tbody>
84
+ </table>
85
+ </div>
86
+
87
+ {% endif %}
88
+
89
+ {% if logs.show_options %}
90
+ <div id="wprss-error-log-options" class="wpra-slide-box wpra-slide-box-open">
91
+ <form id="wprss-log-options-form" method="POST">
92
+ {{ wp_nonce_field(logs.nonce) }}
93
+ <h4>{% trans "Log Options" %}</h4>
94
+ <label>
95
+ <span>{% trans "Enable logging" %}</span>
96
+ <div>
97
+ <input type="checkbox" name="logging_enabled"
98
+ value="1" {% if logs.config['logging/enabled'] %} checked {% endif %} />
99
+ </div>
100
+ </label>
101
+ <label>
102
+ <span>{% trans "Auto delete logs older than" %}</span>
103
+ <div>
104
+ <input type="number" name="logging_limit_days" value="{{ logs.config['logging/limit_days'] }}"
105
+ min="0" />
106
+ <span>{% trans "days" %}</span>
107
+ <i>{% trans "(Once per day, logs older than the set amount of days are deleted)" %}</i>
108
+ </div>
109
+ </label>
110
+
111
+ <div class="wpra-submit-wrap">
112
+ <button type="submit" name="wpra-log-options" class="button" value="1">
113
+ {% trans "Save Log Options" %}
114
+ </button>
115
+ </div>
116
+ </form>
117
+ </div>
118
+ {% endif %}
119
+ </div>
templates/admin/tools/main.twig ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <div class="wrap">
2
+ <h1>{% trans "Tools" %}</h1>
3
+
4
+ <h2 class="nav-tab-wrapper">
5
+ {% for key, info in tools %}
6
+ <a href="javascript: void(0)"
7
+ class="nav-tab wpra-tool-tab {{ key == activeTab ? 'nav-tab-active' : '' }}"
8
+ data-wpra-tool="{{ key|e('html_attr') }}"
9
+ >
10
+ {{ info.name }}
11
+ </a>
12
+ {% endfor %}
13
+ </h2>
14
+
15
+ <div class="wpra-tools-container">
16
+ {% for key, info in tools %}
17
+ <div class="wpra-tool" data-wpra-tool="{{ key|e('html_attr') }}">
18
+ {{ info.html|raw }}
19
+ </div>
20
+ {% endfor %}
21
+ </div>
22
+ </div>
23
+
24
+ <style type="text/css">
25
+ .wpra-tools-container > div.wpra-tool {
26
+ display: none;
27
+ }
28
+ </style>
templates/admin/tools/reset.twig ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <h3>{% trans "Delete all imported items" %}</h3>
2
+ <p>{% trans "Click the button below to delete all imported items and posts." %}</p>
3
+ <p>
4
+ {% trans "Kindly note that old items may no longer be present in their respective RSS feeds, meaning that you may be unable to re-import them later." %}
5
+ </p>
6
+
7
+ <form method="post" id="wpra-delete-items-form">
8
+ {{ wp_nonce_field('wpra_delete_all_items', 'wpra_delete_all_items_nonce') }}
9
+ <input type="hidden" name="wpra_delete_all_items" value="1" />
10
+
11
+ <input type="submit"
12
+ class="button button-red"
13
+ name="wpra_delete_all_items_submit"
14
+ value="{{ "Delete all imported items"|trans|e('html_attr') }}"
15
+ />
16
+ </form>
17
+
18
+ <h3>{% trans 'Restore Default Settings' %}</h3>
19
+
20
+ <p>{% trans 'Click the red button to reset the plugin settings to default.' %}</p>
21
+ <p>{% trans 'It is recommended to export your settings before resetting them, in case you need to rollback.' %}</p>
22
+
23
+ <form method="post" id="wpra-reset-settings-form">
24
+ {{ wp_nonce_field('wpra_reset_settings', 'wpra_reset_settings_nonce') }}
25
+ <input type="hidden" name="wpra_reset_settings" value="1" />
26
+
27
+ <input type="submit"
28
+ class="button button-red"
29
+ name="wpra_reset_settings_submit"
30
+ value="{{ "Reset Settings to Default"|trans|e('html_attr') }}"
31
+ />
32
+ </form>
templates/admin/tools/sys_info.twig ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <h3 class="wpra-inline-header">
2
+ {% trans "System Info" %}
3
+ </h3>
4
+
5
+ <form method="post" class="wpra-header-form">
6
+ {{ wp_nonce_field('wpra_dl_sys_info', 'wpra_dl_sys_info_nonce') }}
7
+ <input type="hidden" name="wpra_dl_sysinfo" value="1" />
8
+
9
+ <button type="submit" id="wprss-download-sysinfo" class="button button-primary wpra-header-button">
10
+ <i class="fa fa-download"></i>
11
+ {% trans "Download" %}
12
+ </button>
13
+ </form>
14
+
15
+ <textarea id="wpra-system-info-textarea"
16
+ name="wpra_sysinfo"
17
+ title="{% trans "To copy the system info, double click it and then press Ctrl + C (PC) or Cmd + C (Mac)." %}"
18
+ readonly="readonly"
19
+ ondblclick="this.focus();this.select()">{{ sys_info|raw }}</textarea>
test/bootstrap.php DELETED
@@ -1,21 +0,0 @@
1
- <?php
2
-
3
- global $vendorDir;
4
- $vendorDir = __DIR__ . '/../vendor/';
5
-
6
- require_once $vendorDir . 'autoload.php';
7
-
8
- /**
9
- * Loads a WordPress file.
10
- *
11
- * @since [*next-version*]
12
- *
13
- * @param string $relPath The path relative to a WordPress installation's root directory.
14
- */
15
- function wpraTestLoadWpFile($relPath)
16
- {
17
- global $vendorDir;
18
-
19
- require_once $vendorDir . 'johnpbloch/wordpress-core/' . $relPath;
20
- }
21
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
vendor/autoload.php CHANGED
@@ -4,4 +4,4 @@
4
 
5
  require_once __DIR__ . '/composer/autoload_real.php';
6
 
7
- return ComposerAutoloaderInit78c9546730fedf778e75f7ce2d8c83db::getLoader();
4
 
5
  require_once __DIR__ . '/composer/autoload_real.php';
6
 
7
+ return ComposerAutoloaderInitc370d92a64f00baf5b2602bffa845945::getLoader();
vendor/composer/autoload_classmap.php CHANGED
@@ -286,6 +286,7 @@ return array(
286
  'RebelCode\\Entities\\Api\\StoreInterface' => $baseDir . '/lib/Entities/Api/StoreInterface.php',
287
  'RebelCode\\Entities\\Entity' => $baseDir . '/lib/Entities/Entity.php',
288
  'RebelCode\\Entities\\Properties\\AbstractDecoratorProperty' => $baseDir . '/lib/Entities/Properties/AbstractDecoratorProperty.php',
 
289
  'RebelCode\\Entities\\Properties\\CallbackDecoratorProperty' => $baseDir . '/lib/Entities/Properties/CallbackDecoratorProperty.php',
290
  'RebelCode\\Entities\\Properties\\CallbackProperty' => $baseDir . '/lib/Entities/Properties/CallbackProperty.php',
291
  'RebelCode\\Entities\\Properties\\DefaultingProperty' => $baseDir . '/lib/Entities/Properties/DefaultingProperty.php',
@@ -323,6 +324,7 @@ return array(
323
  'RebelCode\\Wpra\\Core\\Database\\NullTable' => $baseDir . '/src/Database/NullTable.php',
324
  'RebelCode\\Wpra\\Core\\Database\\TableInterface' => $baseDir . '/src/Database/TableInterface.php',
325
  'RebelCode\\Wpra\\Core\\Database\\WpdbTable' => $baseDir . '/src/Database/WpdbTable.php',
 
326
  'RebelCode\\Wpra\\Core\\Entities\\Collections\\FeedItemCollection' => $baseDir . '/src/Entities/Collections/FeedItemCollection.php',
327
  'RebelCode\\Wpra\\Core\\Entities\\Collections\\FeedSourceCollection' => $baseDir . '/src/Entities/Collections/FeedSourceCollection.php',
328
  'RebelCode\\Wpra\\Core\\Entities\\Collections\\FeedTemplateCollection' => $baseDir . '/src/Entities/Collections/FeedTemplateCollection.php',
@@ -332,6 +334,9 @@ return array(
332
  'RebelCode\\Wpra\\Core\\Entities\\Properties\\TimestampProperty' => $baseDir . '/src/Entities/Properties/TimestampProperty.php',
333
  'RebelCode\\Wpra\\Core\\Entities\\Properties\\WpFtImageUrlProperty' => $baseDir . '/src/Entities/Properties/WpFtImageUrlProperty.php',
334
  'RebelCode\\Wpra\\Core\\Entities\\Properties\\WpPostEntityProperty' => $baseDir . '/src/Entities/Properties/WpPostEntityProperty.php',
 
 
 
335
  'RebelCode\\Wpra\\Core\\Entities\\Properties\\WpraSourceDefaultProperty' => $baseDir . '/src/Entities/Properties/WpraSourceDefaultProperty.php',
336
  'RebelCode\\Wpra\\Core\\Entities\\Stores\\BuiltInTemplateStore' => $baseDir . '/src/Entities/Stores/BuiltInTemplateStore.php',
337
  'RebelCode\\Wpra\\Core\\Entities\\Stores\\WpOptionsArrayStore' => $baseDir . '/src/Entities/Stores/WpOptionsArrayStore.php',
@@ -342,7 +347,7 @@ return array(
342
  'RebelCode\\Wpra\\Core\\Handlers\\AddCptMetaCapsHandler' => $baseDir . '/src/Handlers/AddCptMetaCapsHandler.php',
343
  'RebelCode\\Wpra\\Core\\Handlers\\CustomFeed\\RegisterCustomFeedHandler' => $baseDir . '/src/Handlers/CustomFeed/RegisterCustomFeedHandler.php',
344
  'RebelCode\\Wpra\\Core\\Handlers\\CustomFeed\\RenderCustomFeedHandler' => $baseDir . '/src/Handlers/CustomFeed/RenderCustomFeedHandler.php',
345
- 'RebelCode\\Wpra\\Core\\Handlers\\FeedBlacklist\\SaveBlacklistHandler' => $baseDir . '/src/Handlers/FeedBlacklist/SaveBlacklistHandler.php',
346
  'RebelCode\\Wpra\\Core\\Handlers\\FeedShortcode\\FeedsShortcodeHandler' => $baseDir . '/src/Handlers/FeedShortcode/FeedsShortcodeHandler.php',
347
  'RebelCode\\Wpra\\Core\\Handlers\\FeedSources\\FeedSourceSaveMetaHandler' => $baseDir . '/src/Handlers/FeedSources/FeedSourceSaveMetaHandler.php',
348
  'RebelCode\\Wpra\\Core\\Handlers\\FeedSources\\RenderFeedSourceContentHandler' => $baseDir . '/src/Handlers/FeedSources/RenderFeedSourceContentHandler.php',
@@ -373,6 +378,7 @@ return array(
373
  'RebelCode\\Wpra\\Core\\Handlers\\RegisterMetaBoxHandler' => $baseDir . '/src/Handlers/RegisterMetaBoxHandler.php',
374
  'RebelCode\\Wpra\\Core\\Handlers\\RegisterShortcodeHandler' => $baseDir . '/src/Handlers/RegisterShortcodeHandler.php',
375
  'RebelCode\\Wpra\\Core\\Handlers\\RegisterSubMenuPageHandler' => $baseDir . '/src/Handlers/RegisterSubMenuPageHandler.php',
 
376
  'RebelCode\\Wpra\\Core\\Handlers\\RenderTemplateHandler' => $baseDir . '/src/Handlers/RenderTemplateHandler.php',
377
  'RebelCode\\Wpra\\Core\\Handlers\\ScheduleCronJobHandler' => $baseDir . '/src/Handlers/ScheduleCronJobHandler.php',
378
  'RebelCode\\Wpra\\Core\\Importer\\Images\\FbImageContainer' => $baseDir . '/src/Importer/Images/FbImageContainer.php',
@@ -390,6 +396,8 @@ return array(
390
  'RebelCode\\Wpra\\Core\\ModularModule' => $baseDir . '/src/ModularModule.php',
391
  'RebelCode\\Wpra\\Core\\Modules\\AddonsModule' => $baseDir . '/src/Modules/AddonsModule.php',
392
  'RebelCode\\Wpra\\Core\\Modules\\AssetsModule' => $baseDir . '/src/Modules/AssetsModule.php',
 
 
393
  'RebelCode\\Wpra\\Core\\Modules\\CoreModule' => $baseDir . '/src/Modules/CoreModule.php',
394
  'RebelCode\\Wpra\\Core\\Modules\\CustomFeedModule' => $baseDir . '/src/Modules/CustomFeedModule.php',
395
  'RebelCode\\Wpra\\Core\\Modules\\FeedBlacklistModule' => $baseDir . '/src/Modules/FeedBlacklistModule.php',
@@ -401,15 +409,20 @@ return array(
401
  'RebelCode\\Wpra\\Core\\Modules\\GutenbergBlockModule' => $baseDir . '/src/Modules/GutenbergBlockModule.php',
402
  'RebelCode\\Wpra\\Core\\Modules\\I18nModule' => $baseDir . '/src/Modules/I18nModule.php',
403
  'RebelCode\\Wpra\\Core\\Modules\\ImagesModule' => $baseDir . '/src/Modules/ImagesModule.php',
 
404
  'RebelCode\\Wpra\\Core\\Modules\\ImporterModule' => $baseDir . '/src/Modules/ImporterModule.php',
405
  'RebelCode\\Wpra\\Core\\Modules\\LicensingModule' => $baseDir . '/src/Modules/LicensingModule.php',
406
  'RebelCode\\Wpra\\Core\\Modules\\LoggerModule' => $baseDir . '/src/Modules/LoggerModule.php',
 
407
  'RebelCode\\Wpra\\Core\\Modules\\LoremModule' => $baseDir . '/src/Modules/LoremModule.php',
408
  'RebelCode\\Wpra\\Core\\Modules\\ModuleInterface' => $baseDir . '/src/Modules/ModuleInterface.php',
409
  'RebelCode\\Wpra\\Core\\Modules\\ParsedownModule' => $baseDir . '/src/Modules/ParsedownModule.php',
410
  'RebelCode\\Wpra\\Core\\Modules\\PolyLangCompatModule' => $baseDir . '/src/Modules/PolyLangCompatModule.php',
 
411
  'RebelCode\\Wpra\\Core\\Modules\\RestApiModule' => $baseDir . '/src/Modules/RestApiModule.php',
412
  'RebelCode\\Wpra\\Core\\Modules\\SettingsModule' => $baseDir . '/src/Modules/SettingsModule.php',
 
 
413
  'RebelCode\\Wpra\\Core\\Modules\\TwigModule' => $baseDir . '/src/Modules/TwigModule.php',
414
  'RebelCode\\Wpra\\Core\\Modules\\UpsellModule' => $baseDir . '/src/Modules/UpsellModule.php',
415
  'RebelCode\\Wpra\\Core\\Modules\\WpModule' => $baseDir . '/src/Modules/WpModule.php',
@@ -443,6 +456,7 @@ return array(
443
  'RebelCode\\Wpra\\Core\\Twig\\Extensions\\I18n\\I18nTransTokenParser' => $baseDir . '/src/Twig/Extensions/I18n/I18nTransTokenParser.php',
444
  'RebelCode\\Wpra\\Core\\Twig\\Extensions\\I18n\\WpI18nExtension' => $baseDir . '/src/Twig/Extensions/I18n/WpI18nExtension.php',
445
  'RebelCode\\Wpra\\Core\\Twig\\Extensions\\WpraExtension' => $baseDir . '/src/Twig/Extensions/WpraExtension.php',
 
446
  'RebelCode\\Wpra\\Core\\Util\\CallbackIterator' => $baseDir . '/src/Util/CallbackIterator.php',
447
  'RebelCode\\Wpra\\Core\\Util\\IteratorDelegateTrait' => $baseDir . '/src/Util/IteratorDelegateTrait.php',
448
  'RebelCode\\Wpra\\Core\\Util\\MergedIterator' => $baseDir . '/src/Util/MergedIterator.php',
@@ -450,7 +464,7 @@ return array(
450
  'RebelCode\\Wpra\\Core\\Util\\NullFunction' => $baseDir . '/src/Util/NullFunction.php',
451
  'RebelCode\\Wpra\\Core\\Util\\PaginatedIterator' => $baseDir . '/src/Util/PaginatedIterator.php',
452
  'RebelCode\\Wpra\\Core\\Util\\ParseArgsWithSchemaCapableTrait' => $baseDir . '/src/Util/ParseArgsWithSchemaCapableTrait.php',
453
- 'RebelCode\\Wpra\\Core\\Util\\SanitizeIdCommaListCapableTrait' => $baseDir . '/src/Util/SanitizeIdCommaListCapableTrait.php',
454
  'RebelCode\\Wpra\\Core\\Util\\SanitizerInterface' => $baseDir . '/src/Util/SanitizerInterface.php',
455
  'RebelCode\\Wpra\\Core\\Util\\Sanitizers\\BoolSanitizer' => $baseDir . '/src/Util/Sanitizers/BoolSanitizer.php',
456
  'RebelCode\\Wpra\\Core\\Util\\Sanitizers\\CallbackSanitizer' => $baseDir . '/src/Util/Sanitizers/CallbackSanitizer.php',
286
  'RebelCode\\Entities\\Api\\StoreInterface' => $baseDir . '/lib/Entities/Api/StoreInterface.php',
287
  'RebelCode\\Entities\\Entity' => $baseDir . '/lib/Entities/Entity.php',
288
  'RebelCode\\Entities\\Properties\\AbstractDecoratorProperty' => $baseDir . '/lib/Entities/Properties/AbstractDecoratorProperty.php',
289
+ 'RebelCode\\Entities\\Properties\\AliasProperty' => $baseDir . '/lib/Entities/Properties/AliasProperty.php',
290
  'RebelCode\\Entities\\Properties\\CallbackDecoratorProperty' => $baseDir . '/lib/Entities/Properties/CallbackDecoratorProperty.php',
291
  'RebelCode\\Entities\\Properties\\CallbackProperty' => $baseDir . '/lib/Entities/Properties/CallbackProperty.php',
292
  'RebelCode\\Entities\\Properties\\DefaultingProperty' => $baseDir . '/lib/Entities/Properties/DefaultingProperty.php',
324
  'RebelCode\\Wpra\\Core\\Database\\NullTable' => $baseDir . '/src/Database/NullTable.php',
325
  'RebelCode\\Wpra\\Core\\Database\\TableInterface' => $baseDir . '/src/Database/TableInterface.php',
326
  'RebelCode\\Wpra\\Core\\Database\\WpdbTable' => $baseDir . '/src/Database/WpdbTable.php',
327
+ 'RebelCode\\Wpra\\Core\\Entities\\Collections\\FeedBlacklistCollection' => $baseDir . '/src/Entities/Collections/FeedBlacklistCollection.php',
328
  'RebelCode\\Wpra\\Core\\Entities\\Collections\\FeedItemCollection' => $baseDir . '/src/Entities/Collections/FeedItemCollection.php',
329
  'RebelCode\\Wpra\\Core\\Entities\\Collections\\FeedSourceCollection' => $baseDir . '/src/Entities/Collections/FeedSourceCollection.php',
330
  'RebelCode\\Wpra\\Core\\Entities\\Collections\\FeedTemplateCollection' => $baseDir . '/src/Entities/Collections/FeedTemplateCollection.php',
334
  'RebelCode\\Wpra\\Core\\Entities\\Properties\\TimestampProperty' => $baseDir . '/src/Entities/Properties/TimestampProperty.php',
335
  'RebelCode\\Wpra\\Core\\Entities\\Properties\\WpFtImageUrlProperty' => $baseDir . '/src/Entities/Properties/WpFtImageUrlProperty.php',
336
  'RebelCode\\Wpra\\Core\\Entities\\Properties\\WpPostEntityProperty' => $baseDir . '/src/Entities/Properties/WpPostEntityProperty.php',
337
+ 'RebelCode\\Wpra\\Core\\Entities\\Properties\\WpPostPermalinkProperty' => $baseDir . '/src/Entities/Properties/WpPostPermalinkProperty.php',
338
+ 'RebelCode\\Wpra\\Core\\Entities\\Properties\\WpraItemSourceProperty' => $baseDir . '/src/Entities/Properties/WpraItemSourceProperty.php',
339
+ 'RebelCode\\Wpra\\Core\\Entities\\Properties\\WpraPostTypeDependentProperty' => $baseDir . '/src/Entities/Properties/WpraPostTypeDependentProperty.php',
340
  'RebelCode\\Wpra\\Core\\Entities\\Properties\\WpraSourceDefaultProperty' => $baseDir . '/src/Entities/Properties/WpraSourceDefaultProperty.php',
341
  'RebelCode\\Wpra\\Core\\Entities\\Stores\\BuiltInTemplateStore' => $baseDir . '/src/Entities/Stores/BuiltInTemplateStore.php',
342
  'RebelCode\\Wpra\\Core\\Entities\\Stores\\WpOptionsArrayStore' => $baseDir . '/src/Entities/Stores/WpOptionsArrayStore.php',
347
  'RebelCode\\Wpra\\Core\\Handlers\\AddCptMetaCapsHandler' => $baseDir . '/src/Handlers/AddCptMetaCapsHandler.php',
348
  'RebelCode\\Wpra\\Core\\Handlers\\CustomFeed\\RegisterCustomFeedHandler' => $baseDir . '/src/Handlers/CustomFeed/RegisterCustomFeedHandler.php',
349
  'RebelCode\\Wpra\\Core\\Handlers\\CustomFeed\\RenderCustomFeedHandler' => $baseDir . '/src/Handlers/CustomFeed/RenderCustomFeedHandler.php',
350
+ 'RebelCode\\Wpra\\Core\\Handlers\\EchoHandler' => $baseDir . '/src/Handlers/EchoHandler.php',
351
  'RebelCode\\Wpra\\Core\\Handlers\\FeedShortcode\\FeedsShortcodeHandler' => $baseDir . '/src/Handlers/FeedShortcode/FeedsShortcodeHandler.php',
352
  'RebelCode\\Wpra\\Core\\Handlers\\FeedSources\\FeedSourceSaveMetaHandler' => $baseDir . '/src/Handlers/FeedSources/FeedSourceSaveMetaHandler.php',
353
  'RebelCode\\Wpra\\Core\\Handlers\\FeedSources\\RenderFeedSourceContentHandler' => $baseDir . '/src/Handlers/FeedSources/RenderFeedSourceContentHandler.php',
378
  'RebelCode\\Wpra\\Core\\Handlers\\RegisterMetaBoxHandler' => $baseDir . '/src/Handlers/RegisterMetaBoxHandler.php',
379
  'RebelCode\\Wpra\\Core\\Handlers\\RegisterShortcodeHandler' => $baseDir . '/src/Handlers/RegisterShortcodeHandler.php',
380
  'RebelCode\\Wpra\\Core\\Handlers\\RegisterSubMenuPageHandler' => $baseDir . '/src/Handlers/RegisterSubMenuPageHandler.php',
381
+ 'RebelCode\\Wpra\\Core\\Handlers\\RenderMetaBoxTemplateHandler' => $baseDir . '/src/Handlers/RenderMetaBoxTemplateHandler.php',
382
  'RebelCode\\Wpra\\Core\\Handlers\\RenderTemplateHandler' => $baseDir . '/src/Handlers/RenderTemplateHandler.php',
383
  'RebelCode\\Wpra\\Core\\Handlers\\ScheduleCronJobHandler' => $baseDir . '/src/Handlers/ScheduleCronJobHandler.php',
384
  'RebelCode\\Wpra\\Core\\Importer\\Images\\FbImageContainer' => $baseDir . '/src/Importer/Images/FbImageContainer.php',
396
  'RebelCode\\Wpra\\Core\\ModularModule' => $baseDir . '/src/ModularModule.php',
397
  'RebelCode\\Wpra\\Core\\Modules\\AddonsModule' => $baseDir . '/src/Modules/AddonsModule.php',
398
  'RebelCode\\Wpra\\Core\\Modules\\AssetsModule' => $baseDir . '/src/Modules/AssetsModule.php',
399
+ 'RebelCode\\Wpra\\Core\\Modules\\BlacklistToolModule' => $baseDir . '/src/Modules/BlacklistToolModule.php',
400
+ 'RebelCode\\Wpra\\Core\\Modules\\BulkAddToolModule' => $baseDir . '/src/Modules/BulkAddToolModule.php',
401
  'RebelCode\\Wpra\\Core\\Modules\\CoreModule' => $baseDir . '/src/Modules/CoreModule.php',
402
  'RebelCode\\Wpra\\Core\\Modules\\CustomFeedModule' => $baseDir . '/src/Modules/CustomFeedModule.php',
403
  'RebelCode\\Wpra\\Core\\Modules\\FeedBlacklistModule' => $baseDir . '/src/Modules/FeedBlacklistModule.php',
409
  'RebelCode\\Wpra\\Core\\Modules\\GutenbergBlockModule' => $baseDir . '/src/Modules/GutenbergBlockModule.php',
410
  'RebelCode\\Wpra\\Core\\Modules\\I18nModule' => $baseDir . '/src/Modules/I18nModule.php',
411
  'RebelCode\\Wpra\\Core\\Modules\\ImagesModule' => $baseDir . '/src/Modules/ImagesModule.php',
412
+ 'RebelCode\\Wpra\\Core\\Modules\\ImportExportToolsModule' => $baseDir . '/src/Modules/ImportExportToolsModule.php',
413
  'RebelCode\\Wpra\\Core\\Modules\\ImporterModule' => $baseDir . '/src/Modules/ImporterModule.php',
414
  'RebelCode\\Wpra\\Core\\Modules\\LicensingModule' => $baseDir . '/src/Modules/LicensingModule.php',
415
  'RebelCode\\Wpra\\Core\\Modules\\LoggerModule' => $baseDir . '/src/Modules/LoggerModule.php',
416
+ 'RebelCode\\Wpra\\Core\\Modules\\LogsToolModule' => $baseDir . '/src/Modules/LogsToolModule.php',
417
  'RebelCode\\Wpra\\Core\\Modules\\LoremModule' => $baseDir . '/src/Modules/LoremModule.php',
418
  'RebelCode\\Wpra\\Core\\Modules\\ModuleInterface' => $baseDir . '/src/Modules/ModuleInterface.php',
419
  'RebelCode\\Wpra\\Core\\Modules\\ParsedownModule' => $baseDir . '/src/Modules/ParsedownModule.php',
420
  'RebelCode\\Wpra\\Core\\Modules\\PolyLangCompatModule' => $baseDir . '/src/Modules/PolyLangCompatModule.php',
421
+ 'RebelCode\\Wpra\\Core\\Modules\\ResetToolModule' => $baseDir . '/src/Modules/ResetToolModule.php',
422
  'RebelCode\\Wpra\\Core\\Modules\\RestApiModule' => $baseDir . '/src/Modules/RestApiModule.php',
423
  'RebelCode\\Wpra\\Core\\Modules\\SettingsModule' => $baseDir . '/src/Modules/SettingsModule.php',
424
+ 'RebelCode\\Wpra\\Core\\Modules\\SysInfoToolModule' => $baseDir . '/src/Modules/SysInfoToolModule.php',
425
+ 'RebelCode\\Wpra\\Core\\Modules\\ToolsModule' => $baseDir . '/src/Modules/ToolsModule.php',
426
  'RebelCode\\Wpra\\Core\\Modules\\TwigModule' => $baseDir . '/src/Modules/TwigModule.php',
427
  'RebelCode\\Wpra\\Core\\Modules\\UpsellModule' => $baseDir . '/src/Modules/UpsellModule.php',
428
  'RebelCode\\Wpra\\Core\\Modules\\WpModule' => $baseDir . '/src/Modules/WpModule.php',
456
  'RebelCode\\Wpra\\Core\\Twig\\Extensions\\I18n\\I18nTransTokenParser' => $baseDir . '/src/Twig/Extensions/I18n/I18nTransTokenParser.php',
457
  'RebelCode\\Wpra\\Core\\Twig\\Extensions\\I18n\\WpI18nExtension' => $baseDir . '/src/Twig/Extensions/I18n/WpI18nExtension.php',
458
  'RebelCode\\Wpra\\Core\\Twig\\Extensions\\WpraExtension' => $baseDir . '/src/Twig/Extensions/WpraExtension.php',
459
+ 'RebelCode\\Wpra\\Core\\Ui\\BlacklistTable' => $baseDir . '/src/Ui/BlacklistTable.php',
460
  'RebelCode\\Wpra\\Core\\Util\\CallbackIterator' => $baseDir . '/src/Util/CallbackIterator.php',
461
  'RebelCode\\Wpra\\Core\\Util\\IteratorDelegateTrait' => $baseDir . '/src/Util/IteratorDelegateTrait.php',
462
  'RebelCode\\Wpra\\Core\\Util\\MergedIterator' => $baseDir . '/src/Util/MergedIterator.php',
464
  'RebelCode\\Wpra\\Core\\Util\\NullFunction' => $baseDir . '/src/Util/NullFunction.php',
465
  'RebelCode\\Wpra\\Core\\Util\\PaginatedIterator' => $baseDir . '/src/Util/PaginatedIterator.php',
466
  'RebelCode\\Wpra\\Core\\Util\\ParseArgsWithSchemaCapableTrait' => $baseDir . '/src/Util/ParseArgsWithSchemaCapableTrait.php',
467
+ 'RebelCode\\Wpra\\Core\\Util\\SanitizeCommaListCapableTrait' => $baseDir . '/src/Util/SanitizeCommaListCapableTrait.php',
468
  'RebelCode\\Wpra\\Core\\Util\\SanitizerInterface' => $baseDir . '/src/Util/SanitizerInterface.php',
469
  'RebelCode\\Wpra\\Core\\Util\\Sanitizers\\BoolSanitizer' => $baseDir . '/src/Util/Sanitizers/BoolSanitizer.php',
470
  'RebelCode\\Wpra\\Core\\Util\\Sanitizers\\CallbackSanitizer' => $baseDir . '/src/Util/Sanitizers/CallbackSanitizer.php',
vendor/composer/autoload_real.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  // autoload_real.php @generated by Composer
4
 
5
- class ComposerAutoloaderInit78c9546730fedf778e75f7ce2d8c83db
6
  {
7
  private static $loader;
8
 
@@ -19,15 +19,15 @@ class ComposerAutoloaderInit78c9546730fedf778e75f7ce2d8c83db
19
  return self::$loader;
20
  }
21
 
22
- spl_autoload_register(array('ComposerAutoloaderInit78c9546730fedf778e75f7ce2d8c83db', 'loadClassLoader'), true, true);
23
  self::$loader = $loader = new \Composer\Autoload\ClassLoader();
24
- spl_autoload_unregister(array('ComposerAutoloaderInit78c9546730fedf778e75f7ce2d8c83db', 'loadClassLoader'));
25
 
26
  $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
27
  if ($useStaticLoader) {
28
  require_once __DIR__ . '/autoload_static.php';
29
 
30
- call_user_func(\Composer\Autoload\ComposerStaticInit78c9546730fedf778e75f7ce2d8c83db::getInitializer($loader));
31
  } else {
32
  $map = require __DIR__ . '/autoload_namespaces.php';
33
  foreach ($map as $namespace => $path) {
@@ -48,19 +48,19 @@ class ComposerAutoloaderInit78c9546730fedf778e75f7ce2d8c83db
48
  $loader->register(true);
49
 
50
  if ($useStaticLoader) {
51
- $includeFiles = Composer\Autoload\ComposerStaticInit78c9546730fedf778e75f7ce2d8c83db::$files;
52
  } else {
53
  $includeFiles = require __DIR__ . '/autoload_files.php';
54
  }
55
  foreach ($includeFiles as $fileIdentifier => $file) {
56
- composerRequire78c9546730fedf778e75f7ce2d8c83db($fileIdentifier, $file);
57
  }
58
 
59
  return $loader;
60
  }
61
  }
62
 
63
- function composerRequire78c9546730fedf778e75f7ce2d8c83db($fileIdentifier, $file)
64
  {
65
  if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
66
  require $file;
2
 
3
  // autoload_real.php @generated by Composer
4
 
5
+ class ComposerAutoloaderInitc370d92a64f00baf5b2602bffa845945
6
  {
7
  private static $loader;
8
 
19
  return self::$loader;
20
  }
21
 
22
+ spl_autoload_register(array('ComposerAutoloaderInitc370d92a64f00baf5b2602bffa845945', 'loadClassLoader'), true, true);
23
  self::$loader = $loader = new \Composer\Autoload\ClassLoader();
24
+ spl_autoload_unregister(array('ComposerAutoloaderInitc370d92a64f00baf5b2602bffa845945', 'loadClassLoader'));
25
 
26
  $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded());
27
  if ($useStaticLoader) {
28
  require_once __DIR__ . '/autoload_static.php';
29
 
30
+ call_user_func(\Composer\Autoload\ComposerStaticInitc370d92a64f00baf5b2602bffa845945::getInitializer($loader));
31
  } else {
32
  $map = require __DIR__ . '/autoload_namespaces.php';
33
  foreach ($map as $namespace => $path) {
48
  $loader->register(true);
49
 
50
  if ($useStaticLoader) {
51
+ $includeFiles = Composer\Autoload\ComposerStaticInitc370d92a64f00baf5b2602bffa845945::$files;
52
  } else {
53
  $includeFiles = require __DIR__ . '/autoload_files.php';
54
  }
55
  foreach ($includeFiles as $fileIdentifier => $file) {
56
+ composerRequirec370d92a64f00baf5b2602bffa845945($fileIdentifier, $file);
57
  }
58
 
59
  return $loader;
60
  }
61
  }
62
 
63
+ function composerRequirec370d92a64f00baf5b2602bffa845945($fileIdentifier, $file)
64
  {
65
  if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
66
  require $file;
vendor/composer/autoload_static.php CHANGED
@@ -4,7 +4,7 @@
4
 
5
  namespace Composer\Autoload;
6
 
7
- class ComposerStaticInit78c9546730fedf778e75f7ce2d8c83db
8
  {
9
  public static $files = array (
10
  '320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
@@ -474,6 +474,7 @@ class ComposerStaticInit78c9546730fedf778e75f7ce2d8c83db
474
  'RebelCode\\Entities\\Api\\StoreInterface' => __DIR__ . '/../..' . '/lib/Entities/Api/StoreInterface.php',
475
  'RebelCode\\Entities\\Entity' => __DIR__ . '/../..' . '/lib/Entities/Entity.php',
476
  'RebelCode\\Entities\\Properties\\AbstractDecoratorProperty' => __DIR__ . '/../..' . '/lib/Entities/Properties/AbstractDecoratorProperty.php',
 
477
  'RebelCode\\Entities\\Properties\\CallbackDecoratorProperty' => __DIR__ . '/../..' . '/lib/Entities/Properties/CallbackDecoratorProperty.php',
478
  'RebelCode\\Entities\\Properties\\CallbackProperty' => __DIR__ . '/../..' . '/lib/Entities/Properties/CallbackProperty.php',
479
  'RebelCode\\Entities\\Properties\\DefaultingProperty' => __DIR__ . '/../..' . '/lib/Entities/Properties/DefaultingProperty.php',
@@ -511,6 +512,7 @@ class ComposerStaticInit78c9546730fedf778e75f7ce2d8c83db
511
  'RebelCode\\Wpra\\Core\\Database\\NullTable' => __DIR__ . '/../..' . '/src/Database/NullTable.php',
512
  'RebelCode\\Wpra\\Core\\Database\\TableInterface' => __DIR__ . '/../..' . '/src/Database/TableInterface.php',
513
  'RebelCode\\Wpra\\Core\\Database\\WpdbTable' => __DIR__ . '/../..' . '/src/Database/WpdbTable.php',
 
514
  'RebelCode\\Wpra\\Core\\Entities\\Collections\\FeedItemCollection' => __DIR__ . '/../..' . '/src/Entities/Collections/FeedItemCollection.php',
515
  'RebelCode\\Wpra\\Core\\Entities\\Collections\\FeedSourceCollection' => __DIR__ . '/../..' . '/src/Entities/Collections/FeedSourceCollection.php',
516
  'RebelCode\\Wpra\\Core\\Entities\\Collections\\FeedTemplateCollection' => __DIR__ . '/../..' . '/src/Entities/Collections/FeedTemplateCollection.php',
@@ -520,6 +522,9 @@ class ComposerStaticInit78c9546730fedf778e75f7ce2d8c83db
520
  'RebelCode\\Wpra\\Core\\Entities\\Properties\\TimestampProperty' => __DIR__ . '/../..' . '/src/Entities/Properties/TimestampProperty.php',
521
  'RebelCode\\Wpra\\Core\\Entities\\Properties\\WpFtImageUrlProperty' => __DIR__ . '/../..' . '/src/Entities/Properties/WpFtImageUrlProperty.php',
522
  'RebelCode\\Wpra\\Core\\Entities\\Properties\\WpPostEntityProperty' => __DIR__ . '/../..' . '/src/Entities/Properties/WpPostEntityProperty.php',
 
 
 
523
  'RebelCode\\Wpra\\Core\\Entities\\Properties\\WpraSourceDefaultProperty' => __DIR__ . '/../..' . '/src/Entities/Properties/WpraSourceDefaultProperty.php',
524
  'RebelCode\\Wpra\\Core\\Entities\\Stores\\BuiltInTemplateStore' => __DIR__ . '/../..' . '/src/Entities/Stores/BuiltInTemplateStore.php',
525
  'RebelCode\\Wpra\\Core\\Entities\\Stores\\WpOptionsArrayStore' => __DIR__ . '/../..' . '/src/Entities/Stores/WpOptionsArrayStore.php',
@@ -530,7 +535,7 @@ class ComposerStaticInit78c9546730fedf778e75f7ce2d8c83db
530
  'RebelCode\\Wpra\\Core\\Handlers\\AddCptMetaCapsHandler' => __DIR__ . '/../..' . '/src/Handlers/AddCptMetaCapsHandler.php',
531
  'RebelCode\\Wpra\\Core\\Handlers\\CustomFeed\\RegisterCustomFeedHandler' => __DIR__ . '/../..' . '/src/Handlers/CustomFeed/RegisterCustomFeedHandler.php',
532
  'RebelCode\\Wpra\\Core\\Handlers\\CustomFeed\\RenderCustomFeedHandler' => __DIR__ . '/../..' . '/src/Handlers/CustomFeed/RenderCustomFeedHandler.php',
533
- 'RebelCode\\Wpra\\Core\\Handlers\\FeedBlacklist\\SaveBlacklistHandler' => __DIR__ . '/../..' . '/src/Handlers/FeedBlacklist/SaveBlacklistHandler.php',
534
  'RebelCode\\Wpra\\Core\\Handlers\\FeedShortcode\\FeedsShortcodeHandler' => __DIR__ . '/../..' . '/src/Handlers/FeedShortcode/FeedsShortcodeHandler.php',
535
  'RebelCode\\Wpra\\Core\\Handlers\\FeedSources\\FeedSourceSaveMetaHandler' => __DIR__ . '/../..' . '/src/Handlers/FeedSources/FeedSourceSaveMetaHandler.php',
536
  'RebelCode\\Wpra\\Core\\Handlers\\FeedSources\\RenderFeedSourceContentHandler' => __DIR__ . '/../..' . '/src/Handlers/FeedSources/RenderFeedSourceContentHandler.php',
@@ -561,6 +566,7 @@ class ComposerStaticInit78c9546730fedf778e75f7ce2d8c83db
561
  'RebelCode\\Wpra\\Core\\Handlers\\RegisterMetaBoxHandler' => __DIR__ . '/../..' . '/src/Handlers/RegisterMetaBoxHandler.php',
562
  'RebelCode\\Wpra\\Core\\Handlers\\RegisterShortcodeHandler' => __DIR__ . '/../..' . '/src/Handlers/RegisterShortcodeHandler.php',
563
  'RebelCode\\Wpra\\Core\\Handlers\\RegisterSubMenuPageHandler' => __DIR__ . '/../..' . '/src/Handlers/RegisterSubMenuPageHandler.php',
 
564
  'RebelCode\\Wpra\\Core\\Handlers\\RenderTemplateHandler' => __DIR__ . '/../..' . '/src/Handlers/RenderTemplateHandler.php',
565
  'RebelCode\\Wpra\\Core\\Handlers\\ScheduleCronJobHandler' => __DIR__ . '/../..' . '/src/Handlers/ScheduleCronJobHandler.php',
566
  'RebelCode\\Wpra\\Core\\Importer\\Images\\FbImageContainer' => __DIR__ . '/../..' . '/src/Importer/Images/FbImageContainer.php',
@@ -578,6 +584,8 @@ class ComposerStaticInit78c9546730fedf778e75f7ce2d8c83db
578
  'RebelCode\\Wpra\\Core\\ModularModule' => __DIR__ . '/../..' . '/src/ModularModule.php',
579
  'RebelCode\\Wpra\\Core\\Modules\\AddonsModule' => __DIR__ . '/../..' . '/src/Modules/AddonsModule.php',
580
  'RebelCode\\Wpra\\Core\\Modules\\AssetsModule' => __DIR__ . '/../..' . '/src/Modules/AssetsModule.php',
 
 
581
  'RebelCode\\Wpra\\Core\\Modules\\CoreModule' => __DIR__ . '/../..' . '/src/Modules/CoreModule.php',
582
  'RebelCode\\Wpra\\Core\\Modules\\CustomFeedModule' => __DIR__ . '/../..' . '/src/Modules/CustomFeedModule.php',
583
  'RebelCode\\Wpra\\Core\\Modules\\FeedBlacklistModule' => __DIR__ . '/../..' . '/src/Modules/FeedBlacklistModule.php',
@@ -589,15 +597,20 @@ class ComposerStaticInit78c9546730fedf778e75f7ce2d8c83db
589
  'RebelCode\\Wpra\\Core\\Modules\\GutenbergBlockModule' => __DIR__ . '/../..' . '/src/Modules/GutenbergBlockModule.php',
590
  'RebelCode\\Wpra\\Core\\Modules\\I18nModule' => __DIR__ . '/../..' . '/src/Modules/I18nModule.php',
591
  'RebelCode\\Wpra\\Core\\Modules\\ImagesModule' => __DIR__ . '/../..' . '/src/Modules/ImagesModule.php',
 
592
  'RebelCode\\Wpra\\Core\\Modules\\ImporterModule' => __DIR__ . '/../..' . '/src/Modules/ImporterModule.php',
593
  'RebelCode\\Wpra\\Core\\Modules\\LicensingModule' => __DIR__ . '/../..' . '/src/Modules/LicensingModule.php',
594
  'RebelCode\\Wpra\\Core\\Modules\\LoggerModule' => __DIR__ . '/../..' . '/src/Modules/LoggerModule.php',
 
595
  'RebelCode\\Wpra\\Core\\Modules\\LoremModule' => __DIR__ . '/../..' . '/src/Modules/LoremModule.php',
596
  'RebelCode\\Wpra\\Core\\Modules\\ModuleInterface' => __DIR__ . '/../..' . '/src/Modules/ModuleInterface.php',
597
  'RebelCode\\Wpra\\Core\\Modules\\ParsedownModule' => __DIR__ . '/../..' . '/src/Modules/ParsedownModule.php',
598
  'RebelCode\\Wpra\\Core\\Modules\\PolyLangCompatModule' => __DIR__ . '/../..' . '/src/Modules/PolyLangCompatModule.php',
 
599
  'RebelCode\\Wpra\\Core\\Modules\\RestApiModule' => __DIR__ . '/../..' . '/src/Modules/RestApiModule.php',
600
  'RebelCode\\Wpra\\Core\\Modules\\SettingsModule' => __DIR__ . '/../..' . '/src/Modules/SettingsModule.php',
 
 
601
  'RebelCode\\Wpra\\Core\\Modules\\TwigModule' => __DIR__ . '/../..' . '/src/Modules/TwigModule.php',
602
  'RebelCode\\Wpra\\Core\\Modules\\UpsellModule' => __DIR__ . '/../..' . '/src/Modules/UpsellModule.php',
603
  'RebelCode\\Wpra\\Core\\Modules\\WpModule' => __DIR__ . '/../..' . '/src/Modules/WpModule.php',
@@ -631,6 +644,7 @@ class ComposerStaticInit78c9546730fedf778e75f7ce2d8c83db
631
  'RebelCode\\Wpra\\Core\\Twig\\Extensions\\I18n\\I18nTransTokenParser' => __DIR__ . '/../..' . '/src/Twig/Extensions/I18n/I18nTransTokenParser.php',
632
  'RebelCode\\Wpra\\Core\\Twig\\Extensions\\I18n\\WpI18nExtension' => __DIR__ . '/../..' . '/src/Twig/Extensions/I18n/WpI18nExtension.php',
633
  'RebelCode\\Wpra\\Core\\Twig\\Extensions\\WpraExtension' => __DIR__ . '/../..' . '/src/Twig/Extensions/WpraExtension.php',
 
634
  'RebelCode\\Wpra\\Core\\Util\\CallbackIterator' => __DIR__ . '/../..' . '/src/Util/CallbackIterator.php',
635
  'RebelCode\\Wpra\\Core\\Util\\IteratorDelegateTrait' => __DIR__ . '/../..' . '/src/Util/IteratorDelegateTrait.php',
636
  'RebelCode\\Wpra\\Core\\Util\\MergedIterator' => __DIR__ . '/../..' . '/src/Util/MergedIterator.php',
@@ -638,7 +652,7 @@ class ComposerStaticInit78c9546730fedf778e75f7ce2d8c83db
638
  'RebelCode\\Wpra\\Core\\Util\\NullFunction' => __DIR__ . '/../..' . '/src/Util/NullFunction.php',
639
  'RebelCode\\Wpra\\Core\\Util\\PaginatedIterator' => __DIR__ . '/../..' . '/src/Util/PaginatedIterator.php',
640
  'RebelCode\\Wpra\\Core\\Util\\ParseArgsWithSchemaCapableTrait' => __DIR__ . '/../..' . '/src/Util/ParseArgsWithSchemaCapableTrait.php',
641
- 'RebelCode\\Wpra\\Core\\Util\\SanitizeIdCommaListCapableTrait' => __DIR__ . '/../..' . '/src/Util/SanitizeIdCommaListCapableTrait.php',
642
  'RebelCode\\Wpra\\Core\\Util\\SanitizerInterface' => __DIR__ . '/../..' . '/src/Util/SanitizerInterface.php',
643
  'RebelCode\\Wpra\\Core\\Util\\Sanitizers\\BoolSanitizer' => __DIR__ . '/../..' . '/src/Util/Sanitizers/BoolSanitizer.php',
644
  'RebelCode\\Wpra\\Core\\Util\\Sanitizers\\CallbackSanitizer' => __DIR__ . '/../..' . '/src/Util/Sanitizers/CallbackSanitizer.php',
@@ -1063,10 +1077,10 @@ class ComposerStaticInit78c9546730fedf778e75f7ce2d8c83db
1063
  public static function getInitializer(ClassLoader $loader)
1064
  {
1065
  return \Closure::bind(function () use ($loader) {
1066
- $loader->prefixLengthsPsr4 = ComposerStaticInit78c9546730fedf778e75f7ce2d8c83db::$prefixLengthsPsr4;
1067
- $loader->prefixDirsPsr4 = ComposerStaticInit78c9546730fedf778e75f7ce2d8c83db::$prefixDirsPsr4;
1068
- $loader->prefixesPsr0 = ComposerStaticInit78c9546730fedf778e75f7ce2d8c83db::$prefixesPsr0;
1069
- $loader->classMap = ComposerStaticInit78c9546730fedf778e75f7ce2d8c83db::$classMap;
1070
 
1071
  }, null, ClassLoader::class);
1072
  }
4
 
5
  namespace Composer\Autoload;
6
 
7
+ class ComposerStaticInitc370d92a64f00baf5b2602bffa845945
8
  {
9
  public static $files = array (
10
  '320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
474
  'RebelCode\\Entities\\Api\\StoreInterface' => __DIR__ . '/../..' . '/lib/Entities/Api/StoreInterface.php',
475
  'RebelCode\\Entities\\Entity' => __DIR__ . '/../..' . '/lib/Entities/Entity.php',
476
  'RebelCode\\Entities\\Properties\\AbstractDecoratorProperty' => __DIR__ . '/../..' . '/lib/Entities/Properties/AbstractDecoratorProperty.php',
477
+ 'RebelCode\\Entities\\Properties\\AliasProperty' => __DIR__ . '/../..' . '/lib/Entities/Properties/AliasProperty.php',
478
  'RebelCode\\Entities\\Properties\\CallbackDecoratorProperty' => __DIR__ . '/../..' . '/lib/Entities/Properties/CallbackDecoratorProperty.php',
479
  'RebelCode\\Entities\\Properties\\CallbackProperty' => __DIR__ . '/../..' . '/lib/Entities/Properties/CallbackProperty.php',
480
  'RebelCode\\Entities\\Properties\\DefaultingProperty' => __DIR__ . '/../..' . '/lib/Entities/Properties/DefaultingProperty.php',
512
  'RebelCode\\Wpra\\Core\\Database\\NullTable' => __DIR__ . '/../..' . '/src/Database/NullTable.php',
513
  'RebelCode\\Wpra\\Core\\Database\\TableInterface' => __DIR__ . '/../..' . '/src/Database/TableInterface.php',
514
  'RebelCode\\Wpra\\Core\\Database\\WpdbTable' => __DIR__ . '/../..' . '/src/Database/WpdbTable.php',
515
+ 'RebelCode\\Wpra\\Core\\Entities\\Collections\\FeedBlacklistCollection' => __DIR__ . '/../..' . '/src/Entities/Collections/FeedBlacklistCollection.php',
516
  'RebelCode\\Wpra\\Core\\Entities\\Collections\\FeedItemCollection' => __DIR__ . '/../..' . '/src/Entities/Collections/FeedItemCollection.php',
517
  'RebelCode\\Wpra\\Core\\Entities\\Collections\\FeedSourceCollection' => __DIR__ . '/../..' . '/src/Entities/Collections/FeedSourceCollection.php',
518
  'RebelCode\\Wpra\\Core\\Entities\\Collections\\FeedTemplateCollection' => __DIR__ . '/../..' . '/src/Entities/Collections/FeedTemplateCollection.php',
522
  'RebelCode\\Wpra\\Core\\Entities\\Properties\\TimestampProperty' => __DIR__ . '/../..' . '/src/Entities/Properties/TimestampProperty.php',
523
  'RebelCode\\Wpra\\Core\\Entities\\Properties\\WpFtImageUrlProperty' => __DIR__ . '/../..' . '/src/Entities/Properties/WpFtImageUrlProperty.php',
524
  'RebelCode\\Wpra\\Core\\Entities\\Properties\\WpPostEntityProperty' => __DIR__ . '/../..' . '/src/Entities/Properties/WpPostEntityProperty.php',
525
+ 'RebelCode\\Wpra\\Core\\Entities\\Properties\\WpPostPermalinkProperty' => __DIR__ . '/../..' . '/src/Entities/Properties/WpPostPermalinkProperty.php',
526
+ 'RebelCode\\Wpra\\Core\\Entities\\Properties\\WpraItemSourceProperty' => __DIR__ . '/../..' . '/src/Entities/Properties/WpraItemSourceProperty.php',
527
+ 'RebelCode\\Wpra\\Core\\Entities\\Properties\\WpraPostTypeDependentProperty' => __DIR__ . '/../..' . '/src/Entities/Properties/WpraPostTypeDependentProperty.php',
528
  'RebelCode\\Wpra\\Core\\Entities\\Properties\\WpraSourceDefaultProperty' => __DIR__ . '/../..' . '/src/Entities/Properties/WpraSourceDefaultProperty.php',
529
  'RebelCode\\Wpra\\Core\\Entities\\Stores\\BuiltInTemplateStore' => __DIR__ . '/../..' . '/src/Entities/Stores/BuiltInTemplateStore.php',
530
  'RebelCode\\Wpra\\Core\\Entities\\Stores\\WpOptionsArrayStore' => __DIR__ . '/../..' . '/src/Entities/Stores/WpOptionsArrayStore.php',
535
  'RebelCode\\Wpra\\Core\\Handlers\\AddCptMetaCapsHandler' => __DIR__ . '/../..' . '/src/Handlers/AddCptMetaCapsHandler.php',
536
  'RebelCode\\Wpra\\Core\\Handlers\\CustomFeed\\RegisterCustomFeedHandler' => __DIR__ . '/../..' . '/src/Handlers/CustomFeed/RegisterCustomFeedHandler.php',
537
  'RebelCode\\Wpra\\Core\\Handlers\\CustomFeed\\RenderCustomFeedHandler' => __DIR__ . '/../..' . '/src/Handlers/CustomFeed/RenderCustomFeedHandler.php',
538
+ 'RebelCode\\Wpra\\Core\\Handlers\\EchoHandler' => __DIR__ . '/../..' . '/src/Handlers/EchoHandler.php',
539
  'RebelCode\\Wpra\\Core\\Handlers\\FeedShortcode\\FeedsShortcodeHandler' => __DIR__ . '/../..' . '/src/Handlers/FeedShortcode/FeedsShortcodeHandler.php',
540
  'RebelCode\\Wpra\\Core\\Handlers\\FeedSources\\FeedSourceSaveMetaHandler' => __DIR__ . '/../..' . '/src/Handlers/FeedSources/FeedSourceSaveMetaHandler.php',
541
  'RebelCode\\Wpra\\Core\\Handlers\\FeedSources\\RenderFeedSourceContentHandler' => __DIR__ . '/../..' . '/src/Handlers/FeedSources/RenderFeedSourceContentHandler.php',
566
  'RebelCode\\Wpra\\Core\\Handlers\\RegisterMetaBoxHandler' => __DIR__ . '/../..' . '/src/Handlers/RegisterMetaBoxHandler.php',
567
  'RebelCode\\Wpra\\Core\\Handlers\\RegisterShortcodeHandler' => __DIR__ . '/../..' . '/src/Handlers/RegisterShortcodeHandler.php',
568
  'RebelCode\\Wpra\\Core\\Handlers\\RegisterSubMenuPageHandler' => __DIR__ . '/../..' . '/src/Handlers/RegisterSubMenuPageHandler.php',
569
+ 'RebelCode\\Wpra\\Core\\Handlers\\RenderMetaBoxTemplateHandler' => __DIR__ . '/../..' . '/src/Handlers/RenderMetaBoxTemplateHandler.php',
570
  'RebelCode\\Wpra\\Core\\Handlers\\RenderTemplateHandler' => __DIR__ . '/../..' . '/src/Handlers/RenderTemplateHandler.php',
571
  'RebelCode\\Wpra\\Core\\Handlers\\ScheduleCronJobHandler' => __DIR__ . '/../..' . '/src/Handlers/ScheduleCronJobHandler.php',
572
  'RebelCode\\Wpra\\Core\\Importer\\Images\\FbImageContainer' => __DIR__ . '/../..' . '/src/Importer/Images/FbImageContainer.php',
584
  'RebelCode\\Wpra\\Core\\ModularModule' => __DIR__ . '/../..' . '/src/ModularModule.php',
585
  'RebelCode\\Wpra\\Core\\Modules\\AddonsModule' => __DIR__ . '/../..' . '/src/Modules/AddonsModule.php',
586
  'RebelCode\\Wpra\\Core\\Modules\\AssetsModule' => __DIR__ . '/../..' . '/src/Modules/AssetsModule.php',
587
+ 'RebelCode\\Wpra\\Core\\Modules\\BlacklistToolModule' => __DIR__ . '/../..' . '/src/Modules/BlacklistToolModule.php',
588
+ 'RebelCode\\Wpra\\Core\\Modules\\BulkAddToolModule' => __DIR__ . '/../..' . '/src/Modules/BulkAddToolModule.php',
589
  'RebelCode\\Wpra\\Core\\Modules\\CoreModule' => __DIR__ . '/../..' . '/src/Modules/CoreModule.php',
590
  'RebelCode\\Wpra\\Core\\Modules\\CustomFeedModule' => __DIR__ . '/../..' . '/src/Modules/CustomFeedModule.php',
591
  'RebelCode\\Wpra\\Core\\Modules\\FeedBlacklistModule' => __DIR__ . '/../..' . '/src/Modules/FeedBlacklistModule.php',
597
  'RebelCode\\Wpra\\Core\\Modules\\GutenbergBlockModule' => __DIR__ . '/../..' . '/src/Modules/GutenbergBlockModule.php',
598
  'RebelCode\\Wpra\\Core\\Modules\\I18nModule' => __DIR__ . '/../..' . '/src/Modules/I18nModule.php',
599
  'RebelCode\\Wpra\\Core\\Modules\\ImagesModule' => __DIR__ . '/../..' . '/src/Modules/ImagesModule.php',
600
+ 'RebelCode\\Wpra\\Core\\Modules\\ImportExportToolsModule' => __DIR__ . '/../..' . '/src/Modules/ImportExportToolsModule.php',
601
  'RebelCode\\Wpra\\Core\\Modules\\ImporterModule' => __DIR__ . '/../..' . '/src/Modules/ImporterModule.php',
602
  'RebelCode\\Wpra\\Core\\Modules\\LicensingModule' => __DIR__ . '/../..' . '/src/Modules/LicensingModule.php',
603
  'RebelCode\\Wpra\\Core\\Modules\\LoggerModule' => __DIR__ . '/../..' . '/src/Modules/LoggerModule.php',
604
+ 'RebelCode\\Wpra\\Core\\Modules\\LogsToolModule' => __DIR__ . '/../..' . '/src/Modules/LogsToolModule.php',
605
  'RebelCode\\Wpra\\Core\\Modules\\LoremModule' => __DIR__ . '/../..' . '/src/Modules/LoremModule.php',
606
  'RebelCode\\Wpra\\Core\\Modules\\ModuleInterface' => __DIR__ . '/../..' . '/src/Modules/ModuleInterface.php',
607
  'RebelCode\\Wpra\\Core\\Modules\\ParsedownModule' => __DIR__ . '/../..' . '/src/Modules/ParsedownModule.php',
608
  'RebelCode\\Wpra\\Core\\Modules\\PolyLangCompatModule' => __DIR__ . '/../..' . '/src/Modules/PolyLangCompatModule.php',
609
+ 'RebelCode\\Wpra\\Core\\Modules\\ResetToolModule' => __DIR__ . '/../..' . '/src/Modules/ResetToolModule.php',
610
  'RebelCode\\Wpra\\Core\\Modules\\RestApiModule' => __DIR__ . '/../..' . '/src/Modules/RestApiModule.php',
611
  'RebelCode\\Wpra\\Core\\Modules\\SettingsModule' => __DIR__ . '/../..' . '/src/Modules/SettingsModule.php',
612
+ 'RebelCode\\Wpra\\Core\\Modules\\SysInfoToolModule' => __DIR__ . '/../..' . '/src/Modules/SysInfoToolModule.php',
613
+ 'RebelCode\\Wpra\\Core\\Modules\\ToolsModule' => __DIR__ . '/../..' . '/src/Modules/ToolsModule.php',
614
  'RebelCode\\Wpra\\Core\\Modules\\TwigModule' => __DIR__ . '/../..' . '/src/Modules/TwigModule.php',
615
  'RebelCode\\Wpra\\Core\\Modules\\UpsellModule' => __DIR__ . '/../..' . '/src/Modules/UpsellModule.php',
616
  'RebelCode\\Wpra\\Core\\Modules\\WpModule' => __DIR__ . '/../..' . '/src/Modules/WpModule.php',
644
  'RebelCode\\Wpra\\Core\\Twig\\Extensions\\I18n\\I18nTransTokenParser' => __DIR__ . '/../..' . '/src/Twig/Extensions/I18n/I18nTransTokenParser.php',
645
  'RebelCode\\Wpra\\Core\\Twig\\Extensions\\I18n\\WpI18nExtension' => __DIR__ . '/../..' . '/src/Twig/Extensions/I18n/WpI18nExtension.php',
646
  'RebelCode\\Wpra\\Core\\Twig\\Extensions\\WpraExtension' => __DIR__ . '/../..' . '/src/Twig/Extensions/WpraExtension.php',
647
+ 'RebelCode\\Wpra\\Core\\Ui\\BlacklistTable' => __DIR__ . '/../..' . '/src/Ui/BlacklistTable.php',
648
  'RebelCode\\Wpra\\Core\\Util\\CallbackIterator' => __DIR__ . '/../..' . '/src/Util/CallbackIterator.php',
649
  'RebelCode\\Wpra\\Core\\Util\\IteratorDelegateTrait' => __DIR__ . '/../..' . '/src/Util/IteratorDelegateTrait.php',
650
  'RebelCode\\Wpra\\Core\\Util\\MergedIterator' => __DIR__ . '/../..' . '/src/Util/MergedIterator.php',
652
  'RebelCode\\Wpra\\Core\\Util\\NullFunction' => __DIR__ . '/../..' . '/src/Util/NullFunction.php',
653
  'RebelCode\\Wpra\\Core\\Util\\PaginatedIterator' => __DIR__ . '/../..' . '/src/Util/PaginatedIterator.php',
654
  'RebelCode\\Wpra\\Core\\Util\\ParseArgsWithSchemaCapableTrait' => __DIR__ . '/../..' . '/src/Util/ParseArgsWithSchemaCapableTrait.php',
655
+ 'RebelCode\\Wpra\\Core\\Util\\SanitizeCommaListCapableTrait' => __DIR__ . '/../..' . '/src/Util/SanitizeCommaListCapableTrait.php',
656
  'RebelCode\\Wpra\\Core\\Util\\SanitizerInterface' => __DIR__ . '/../..' . '/src/Util/SanitizerInterface.php',
657
  'RebelCode\\Wpra\\Core\\Util\\Sanitizers\\BoolSanitizer' => __DIR__ . '/../..' . '/src/Util/Sanitizers/BoolSanitizer.php',
658
  'RebelCode\\Wpra\\Core\\Util\\Sanitizers\\CallbackSanitizer' => __DIR__ . '/../..' . '/src/Util/Sanitizers/CallbackSanitizer.php',
1077
  public static function getInitializer(ClassLoader $loader)
1078
  {
1079
  return \Closure::bind(function () use ($loader) {
1080
+ $loader->prefixLengthsPsr4 = ComposerStaticInitc370d92a64f00baf5b2602bffa845945::$prefixLengthsPsr4;
1081
+ $loader->prefixDirsPsr4 = ComposerStaticInitc370d92a64f00baf5b2602bffa845945::$prefixDirsPsr4;
1082
+ $loader->prefixesPsr0 = ComposerStaticInitc370d92a64f00baf5b2602bffa845945::$prefixesPsr0;
1083
+ $loader->classMap = ComposerStaticInitc370d92a64f00baf5b2602bffa845945::$classMap;
1084
 
1085
  }, null, ClassLoader::class);
1086
  }
vendor/composer/installed.json CHANGED
@@ -1515,27 +1515,27 @@
1515
  },
1516
  {
1517
  "name": "psr/log",
1518
- "version": "1.1.0",
1519
- "version_normalized": "1.1.0.0",
1520
  "source": {
1521
  "type": "git",
1522
  "url": "https://github.com/php-fig/log.git",
1523
- "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd"
1524
  },
1525
  "dist": {
1526
  "type": "zip",
1527
- "url": "https://api.github.com/repos/php-fig/log/zipball/6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd",
1528
- "reference": "6c001f1daafa3a3ac1d8ff69ee4db8e799a654dd",
1529
  "shasum": ""
1530
  },
1531
  "require": {
1532
  "php": ">=5.3.0"
1533
  },
1534
- "time": "2018-11-20T15:27:04+00:00",
1535
  "type": "library",
1536
  "extra": {
1537
  "branch-alias": {
1538
- "dev-master": "1.0.x-dev"
1539
  }
1540
  },
1541
  "installation-source": "dist",
1515
  },
1516
  {
1517
  "name": "psr/log",
1518
+ "version": "1.1.2",
1519
+ "version_normalized": "1.1.2.0",
1520
  "source": {
1521
  "type": "git",
1522
  "url": "https://github.com/php-fig/log.git",
1523
+ "reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801"
1524
  },
1525
  "dist": {
1526
  "type": "zip",
1527
+ "url": "https://api.github.com/repos/php-fig/log/zipball/446d54b4cb6bf489fc9d75f55843658e6f25d801",
1528
+ "reference": "446d54b4cb6bf489fc9d75f55843658e6f25d801",
1529
  "shasum": ""
1530
  },
1531
  "require": {
1532
  "php": ">=5.3.0"
1533
  },
1534
+ "time": "2019-11-01T11:05:21+00:00",
1535
  "type": "library",
1536
  "extra": {
1537
  "branch-alias": {
1538
+ "dev-master": "1.1.x-dev"
1539
  }
1540
  },
1541
  "installation-source": "dist",
vendor/psr/log/Psr/Log/LoggerInterface.php CHANGED
@@ -118,6 +118,8 @@ interface LoggerInterface
118
  * @param array $context
119
  *
120
  * @return void
 
 
121
  */
122
  public function log($level, $message, array $context = array());
123
  }
118
  * @param array $context
119
  *
120
  * @return void
121
+ *
122
+ * @throws \Psr\Log\InvalidArgumentException
123
  */
124
  public function log($level, $message, array $context = array());
125
  }
vendor/psr/log/Psr/Log/LoggerTrait.php CHANGED
@@ -135,6 +135,8 @@ trait LoggerTrait
135
  * @param array $context
136
  *
137
  * @return void
 
 
138
  */
139
  abstract public function log($level, $message, array $context = array());
140
  }
135
  * @param array $context
136
  *
137
  * @return void
138
+ *
139
+ * @throws \Psr\Log\InvalidArgumentException
140
  */
141
  abstract public function log($level, $message, array $context = array());
142
  }
vendor/psr/log/Psr/Log/NullLogger.php CHANGED
@@ -20,6 +20,8 @@ class NullLogger extends AbstractLogger
20
  * @param array $context
21
  *
22
  * @return void
 
 
23
  */
24
  public function log($level, $message, array $context = array())
25
  {
20
  * @param array $context
21
  *
22
  * @return void
23
+ *
24
+ * @throws \Psr\Log\InvalidArgumentException
25
  */
26
  public function log($level, $message, array $context = array())
27
  {
vendor/psr/log/Psr/Log/Test/LoggerInterfaceTest.php CHANGED
@@ -4,6 +4,7 @@ namespace Psr\Log\Test;
4
 
5
  use Psr\Log\LoggerInterface;
6
  use Psr\Log\LogLevel;
 
7
 
8
  /**
9
  * Provides a base test class for ensuring compliance with the LoggerInterface.
@@ -11,7 +12,7 @@ use Psr\Log\LogLevel;
11
  * Implementors can extend the class and implement abstract methods to run this
12
  * as part of their test suite.
13
  */
14
- abstract class LoggerInterfaceTest extends \PHPUnit_Framework_TestCase
15
  {
16
  /**
17
  * @return LoggerInterface
@@ -140,5 +141,6 @@ class DummyTest
140
  {
141
  public function __toString()
142
  {
 
143
  }
144
  }
4
 
5
  use Psr\Log\LoggerInterface;
6
  use Psr\Log\LogLevel;
7
+ use PHPUnit\Framework\TestCase;
8
 
9
  /**
10
  * Provides a base test class for ensuring compliance with the LoggerInterface.
12
  * Implementors can extend the class and implement abstract methods to run this
13
  * as part of their test suite.
14
  */
15
+ abstract class LoggerInterfaceTest extends TestCase
16
  {
17
  /**
18
  * @return LoggerInterface
141
  {
142
  public function __toString()
143
  {
144
+ return 'DummyTest';
145
  }
146
  }
vendor/psr/log/Psr/Log/Test/TestLogger.php CHANGED
@@ -142,5 +142,6 @@ class TestLogger extends AbstractLogger
142
  public function reset()
143
  {
144
  $this->records = [];
 
145
  }
146
  }
142
  public function reset()
143
  {
144
  $this->records = [];
145
+ $this->recordsByLevel = [];
146
  }
147
  }
vendor/psr/log/composer.json CHANGED
@@ -20,7 +20,7 @@
20
  },
21
  "extra": {
22
  "branch-alias": {
23
- "dev-master": "1.0.x-dev"
24
  }
25
  }
26
  }
20
  },
21
  "extra": {
22
  "branch-alias": {
23
+ "dev-master": "1.1.x-dev"
24
  }
25
  }
26
  }
wp-rss-aggregator.php CHANGED
@@ -4,7 +4,7 @@
4
  * Plugin Name: WP RSS Aggregator
5
  * Plugin URI: https://www.wprssaggregator.com/#utm_source=wpadmin&utm_medium=plugin&utm_campaign=wpraplugin
6
  * Description: Imports and aggregates multiple RSS Feeds.
7
- * Version: 4.16
8
  * Author: RebelCode
9
  * Author URI: https://www.wprssaggregator.com
10
  * Text Domain: wprss
@@ -39,6 +39,8 @@ use RebelCode\Wpra\Core\Container\WpFilterContainer;
39
  use RebelCode\Wpra\Core\ErrorHandler;
40
  use RebelCode\Wpra\Core\Modules\AddonsModule;
41
  use RebelCode\Wpra\Core\Modules\AssetsModule;
 
 
42
  use RebelCode\Wpra\Core\Modules\CoreModule;
43
  use RebelCode\Wpra\Core\Modules\CustomFeedModule;
44
  use RebelCode\Wpra\Core\Modules\FeedBlacklistModule;
@@ -51,14 +53,19 @@ use RebelCode\Wpra\Core\Modules\GutenbergBlockModule;
51
  use RebelCode\Wpra\Core\Modules\I18nModule;
52
  use RebelCode\Wpra\Core\Modules\ImagesModule;
53
  use RebelCode\Wpra\Core\Modules\ImporterModule;
 
54
  use RebelCode\Wpra\Core\Modules\LicensingModule;
55
  use RebelCode\Wpra\Core\Modules\LoggerModule;
 
56
  use RebelCode\Wpra\Core\Modules\LoremModule;
57
  use RebelCode\Wpra\Core\Modules\ModuleInterface;
58
  use RebelCode\Wpra\Core\Modules\ParsedownModule;
59
  use RebelCode\Wpra\Core\Modules\PolyLangCompatModule;
 
60
  use RebelCode\Wpra\Core\Modules\RestApiModule;
61
  use RebelCode\Wpra\Core\Modules\SettingsModule;
 
 
62
  use RebelCode\Wpra\Core\Modules\TwigModule;
63
  use RebelCode\Wpra\Core\Modules\UpsellModule;
64
  use RebelCode\Wpra\Core\Modules\WpModule;
@@ -70,7 +77,7 @@ use RebelCode\Wpra\Core\Plugin;
70
 
71
  // Set the version number of the plugin.
72
  if( !defined( 'WPRSS_VERSION' ) )
73
- define( 'WPRSS_VERSION', '4.16' );
74
 
75
  if( !defined( 'WPRSS_WP_MIN_VERSION' ) )
76
  define( 'WPRSS_WP_MIN_VERSION', '4.8' );
@@ -240,9 +247,6 @@ require_once ( WPRSS_INC . 'admin-options.php' );
240
  /* Load the legacy admin options functions file. */
241
  require_once ( WPRSS_INC . 'admin-options-legacy.php' );
242
 
243
- /* Load the settings import/export file */
244
- require_once ( WPRSS_INC . 'admin-import-export.php' );
245
-
246
  /* Load the debugging file */
247
  require_once ( WPRSS_INC . 'system-info.php' );
248
 
@@ -255,9 +259,6 @@ require_once ( WPRSS_INC . 'OPML.php' );
255
  /* Load the OPML Importer file */
256
  require_once ( WPRSS_INC . 'opml-importer.php' );
257
 
258
- /* Load the admin debugging page file */
259
- require_once ( WPRSS_INC . 'admin-debugging.php' );
260
-
261
  /* Load the admin display-related functions */
262
  require_once ( WPRSS_INC . 'admin-display.php' );
263
 
@@ -315,8 +316,6 @@ require_once ( WPRSS_INC . 'polyfills.php' );
315
  /* Load the youtube functionality */
316
  require_once ( WPRSS_INC . 'youtube.php' );
317
 
318
- do_action('wprss_pre_init');
319
-
320
  register_activation_hook(__FILE__, 'wprss_activate');
321
  register_deactivation_hook(__FILE__, 'wprss_deactivate');
322
 
@@ -396,6 +395,13 @@ function wpra_modules()
396
  'images' => new ImagesModule(),
397
  'custom_feed' => new CustomFeedModule(),
398
  'rest_api' => new RestApiModule(),
 
 
 
 
 
 
 
399
  'settings' => new SettingsModule(),
400
  'licensing' => new LicensingModule(),
401
  'upsell' => new UpsellModule(),
@@ -647,6 +653,7 @@ function wprss() {
647
  }
648
 
649
  try {
 
650
  $instance = wprss();
651
  } catch (Throwable $t) {
652
  wpra_error_handler($t);
4
  * Plugin Name: WP RSS Aggregator
5
  * Plugin URI: https://www.wprssaggregator.com/#utm_source=wpadmin&utm_medium=plugin&utm_campaign=wpraplugin
6
  * Description: Imports and aggregates multiple RSS Feeds.
7
+ * Version: 4.17
8
  * Author: RebelCode
9
  * Author URI: https://www.wprssaggregator.com
10
  * Text Domain: wprss
39
  use RebelCode\Wpra\Core\ErrorHandler;
40
  use RebelCode\Wpra\Core\Modules\AddonsModule;
41
  use RebelCode\Wpra\Core\Modules\AssetsModule;
42
+ use RebelCode\Wpra\Core\Modules\BlacklistToolModule;
43
+ use RebelCode\Wpra\Core\Modules\BulkAddToolModule;
44
  use RebelCode\Wpra\Core\Modules\CoreModule;
45
  use RebelCode\Wpra\Core\Modules\CustomFeedModule;
46
  use RebelCode\Wpra\Core\Modules\FeedBlacklistModule;
53
  use RebelCode\Wpra\Core\Modules\I18nModule;
54
  use RebelCode\Wpra\Core\Modules\ImagesModule;
55
  use RebelCode\Wpra\Core\Modules\ImporterModule;
56
+ use RebelCode\Wpra\Core\Modules\ImportExportToolsModule;
57
  use RebelCode\Wpra\Core\Modules\LicensingModule;
58
  use RebelCode\Wpra\Core\Modules\LoggerModule;
59
+ use RebelCode\Wpra\Core\Modules\LogsToolModule;
60
  use RebelCode\Wpra\Core\Modules\LoremModule;
61
  use RebelCode\Wpra\Core\Modules\ModuleInterface;
62
  use RebelCode\Wpra\Core\Modules\ParsedownModule;
63
  use RebelCode\Wpra\Core\Modules\PolyLangCompatModule;
64
+ use RebelCode\Wpra\Core\Modules\ResetToolModule;
65
  use RebelCode\Wpra\Core\Modules\RestApiModule;
66
  use RebelCode\Wpra\Core\Modules\SettingsModule;
67
+ use RebelCode\Wpra\Core\Modules\SysInfoToolModule;
68
+ use RebelCode\Wpra\Core\Modules\ToolsModule;
69
  use RebelCode\Wpra\Core\Modules\TwigModule;
70
  use RebelCode\Wpra\Core\Modules\UpsellModule;
71
  use RebelCode\Wpra\Core\Modules\WpModule;
77
 
78
  // Set the version number of the plugin.
79
  if( !defined( 'WPRSS_VERSION' ) )
80
+ define( 'WPRSS_VERSION', '4.17' );
81
 
82
  if( !defined( 'WPRSS_WP_MIN_VERSION' ) )
83
  define( 'WPRSS_WP_MIN_VERSION', '4.8' );
247
  /* Load the legacy admin options functions file. */
248
  require_once ( WPRSS_INC . 'admin-options-legacy.php' );
249
 
 
 
 
250
  /* Load the debugging file */
251
  require_once ( WPRSS_INC . 'system-info.php' );
252
 
259
  /* Load the OPML Importer file */
260
  require_once ( WPRSS_INC . 'opml-importer.php' );
261
 
 
 
 
262
  /* Load the admin display-related functions */
263
  require_once ( WPRSS_INC . 'admin-display.php' );
264
 
316
  /* Load the youtube functionality */
317
  require_once ( WPRSS_INC . 'youtube.php' );
318
 
 
 
319
  register_activation_hook(__FILE__, 'wprss_activate');
320
  register_deactivation_hook(__FILE__, 'wprss_deactivate');
321
 
395
  'images' => new ImagesModule(),
396
  'custom_feed' => new CustomFeedModule(),
397
  'rest_api' => new RestApiModule(),
398
+ 'tools' => new ToolsModule(),
399
+ 'tools/bulk_add' => new BulkAddToolModule(),
400
+ 'tools/blacklist' => new BlackListToolModule(),
401
+ 'tools/import_export' => new ImportExportToolsModule(),
402
+ 'tools/logs' => new LogsToolModule(),
403
+ 'tools/sys_info' => new SysInfoToolModule(),
404
+ 'tools/reset' => new ResetToolModule(),
405
  'settings' => new SettingsModule(),
406
  'licensing' => new LicensingModule(),
407
  'upsell' => new UpsellModule(),
653
  }
654
 
655
  try {
656
+ do_action('wprss_pre_init');
657
  $instance = wprss();
658
  } catch (Throwable $t) {
659
  wpra_error_handler($t);