WP RSS Aggregator - Version 4.7.8

Version Description

(2015-11-18) = * Fixed bug: Sticky posts no longer get deleted when truncating, unless imported from a feed source. * Enhanced: Added autoloading and refactored licensing. * Enhanced: Added button to download error log. * Enhanced: Cosmetic changes and fixes.

Download this release

Release Info

Developer markzahra
Plugin Icon 128x128 WP RSS Aggregator
Version 4.7.8
Comparing to
See all releases

Code changes from version 4.7.7 to 4.7.8

css/admin-styles.css CHANGED
@@ -259,6 +259,13 @@ input#thumbnails-width {
259
  display: block;
260
  }
261
 
 
 
 
 
 
 
 
262
 
263
  @media screen and (min-width: 782px) {
264
  #custom_feed_title {
259
  display: block;
260
  }
261
 
262
+ form.wprss-error-log-action {
263
+ display: inline-block;
264
+ margin: 15px 5px 15px 0;
265
+ }
266
+ form.wprss-error-log-action input[type="submit"]:active {
267
+ vertical-align: baseline;
268
+ }
269
 
270
  @media screen and (min-width: 782px) {
271
  #custom_feed_title {
includes/Aventura/Wprss/Core/Caching/ImageCache.php ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Aventura\Wprss\Core\Caching;
4
+
5
+ if (!class_exists('\\WPRSS_Image_Cache')) {
6
+ require WPRSS_INC . 'image-caching.php';
7
+ }
8
+
9
+ /**
10
+ * Image caching class.
11
+ */
12
+ class ImageCache extends \WPRSS_Image_Cache {
13
+
14
+ protected function _construct() {
15
+ $this->set_image_class_name( __NAMESPACE__ . '\\ImageCache\\Image' );
16
+ }
17
+
18
+ }
includes/Aventura/Wprss/Core/Caching/ImageCache/Image.php ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Aventura\Wprss\Core\Caching\ImageCache;
4
+
5
+ if (!class_exists('\\WPRSS_Image_Cache_Image')) {
6
+ require WPRSS_INC . 'image-caching.php';
7
+ }
8
+
9
+ /**
10
+ * Image class for ImageCache module.
11
+ */
12
+ class Image extends \WPRSS_Image_Cache_Image {
13
+
14
+ }
includes/Aventura/Wprss/Core/Licensing/AjaxController.php ADDED
@@ -0,0 +1,192 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Aventura\Wprss\Core\Licensing;
4
+ use \Aventura\Wprss\Core\Licensing\License\Status;
5
+
6
+ /**
7
+ * AJAX controller class for licensing AJAX operations.
8
+ */
9
+ class AjaxController {
10
+
11
+ // Pattern for ajax handler methods
12
+ const AJAX_MANAGE_LICENSE_METHOD_PATTERN = 'handleAjaxLicense%s';
13
+
14
+ protected $_manager;
15
+ protected $_settings;
16
+
17
+ /**
18
+ * Constructor.
19
+ */
20
+ public function __construct() {
21
+ }
22
+
23
+ /**
24
+ * Sets the settings controller to be used by this instance.
25
+ *
26
+ * @param \Aventura\Wprss\Core\Licensing\Settings $settings The settings controller.
27
+ * @return \Aventura\Wprss\Core\Licensing\AjaxController This instance.
28
+ */
29
+ public function setSettingsController( Settings $settings ) {
30
+ $this->_settings = $settings;
31
+ return $this;
32
+ }
33
+
34
+
35
+ /**
36
+ * Gets the settings controller used by this instance.
37
+ *
38
+ * @return \Aventura\Wprss\Core\Licensing\Settings The settings controller used by this instance.
39
+ */
40
+ public function getSettingsController() {
41
+ return $this->_settings;
42
+ }
43
+
44
+
45
+ /**
46
+ * Sets the license manager to be used by this instance.
47
+ *
48
+ * @param \Aventura\Wprss\Core\Licensing\Manager $manager The license manager to use.
49
+ * @return \Aventura\Wprss\Core\Licensing\AjaxController This instance.
50
+ */
51
+ public function setManager(Manager $manager) {
52
+ $this->_manager = $manager;
53
+ return $this;
54
+ }
55
+
56
+
57
+ /**
58
+ * Gets the license manager used by this instance.
59
+ *
60
+ * @return \Aventura\Wprss\Core\Licensing\Manager The lisence manager.
61
+ */
62
+ public function getManager() {
63
+ return $this->_manager;
64
+ }
65
+
66
+ /**
67
+ * Echoes an AJAX error response along with activate/deactivate license button HTML markup and then dies.
68
+ *
69
+ * @param string $message The error message to send.
70
+ * @param string $addonId Optional addon ID related to the error, used for sending the activate/deactivate license button.
71
+ */
72
+ protected function _sendErrorResponse( $message, $addonId = '' ) {
73
+ $response = array(
74
+ 'error' => $message,
75
+ 'html' => $this->getSettingsController()->getActivateLicenseButtonHtml($addonId)
76
+ );
77
+
78
+ echo json_encode( $response );
79
+ die();
80
+ }
81
+
82
+ /**
83
+ * Handles AJAX requests for managing licenses (activation, deactivation, etc..)
84
+ */
85
+ public function handleAjaxManageLicense() {
86
+ // Get data from request
87
+ $nonce = empty( $_GET['nonce'] )? null : sanitize_text_field( $_GET['nonce'] );
88
+ $addon = empty( $_GET['addon'] )? null : sanitize_text_field( $_GET['addon'] );
89
+ $event = empty( $_GET['event'] )? null : sanitize_text_field( $_GET['event'] );
90
+ $licenseKey = empty( $_GET['license'] )? null : sanitize_text_field( $_GET['license'] );
91
+
92
+ // If no nonce, stop
93
+ if ( $nonce === null ) $this->_sendErrorResponse( __( 'No nonce', WPRSS_TEXT_DOMAIN ), $addon );
94
+ // Generate the nonce id
95
+ $nonce_id = sprintf( 'wprss_%s_license_nonce', $addon );
96
+ // Verify the nonce. If verification fails, stop
97
+ if ( ! wp_verify_nonce( $nonce, $nonce_id ) ) {
98
+ $this->_sendErrorResponse( __( 'Bad nonce', WPRSS_TEXT_DOMAIN ), $addon );
99
+ }
100
+
101
+ // Check addon, event and license
102
+ if ( $addon === null ) $this->_sendErrorResponse( __( 'No addon ID', WPRSS_TEXT_DOMAIN ) );
103
+ if ( $event === null ) $this->_sendErrorResponse( __( 'No event specified', WPRSS_TEXT_DOMAIN ), $addon );
104
+ if ( $licenseKey === null ) $this->_sendErrorResponse( __( 'No license', WPRSS_TEXT_DOMAIN ), $addon );
105
+
106
+ $settings = $this->getSettingsController();
107
+ $manager = $this->getManager();
108
+
109
+ // Check if the license key was obfuscated on the client's end.
110
+ if ( $settings->isLicenseKeyObfuscated( $licenseKey ) ) {
111
+ // If so, use the stored license key since obfuscation signifies that the key was not modified
112
+ // and is equal to the one saved in db
113
+ $licenseKey = $manager->getLicense( $addon );
114
+ } else {
115
+ // Otherwise, update the value in db
116
+ $license = $manager->getLicense( $addon );
117
+ if ( $license === null ) {
118
+ $license = $manager->createLicense( $addon );
119
+ }
120
+ $license->setKey( $licenseKey );
121
+ $manager->saveLicenseKeys();
122
+ }
123
+
124
+ // uppercase first letter of event
125
+ $event = ucfirst( $event );
126
+ // Generate method name
127
+ $eventMethod = sprintf( self::AJAX_MANAGE_LICENSE_METHOD_PATTERN, $event );
128
+ // check if the event is handle-able
129
+ if ( ! method_exists( $this, $eventMethod ) ) {
130
+ $this->_sendErrorResponse( __( 'Invalid event specified', WPRSS_TEXT_DOMAIN ), $addon);
131
+ }
132
+
133
+ // Call the appropriate handler method
134
+ $returnValue = call_user_func_array( array( $this, $eventMethod ), array( $addon ) );
135
+
136
+ // Prepare the response
137
+ $partialResponse = array(
138
+ 'addon' => $addon,
139
+ 'html' => $settings->getActivateLicenseButtonHtml( $addon ),
140
+ 'licensedAddons' => array_keys( $manager->getLicensesWithStatus( Status::VALID ) )
141
+ );
142
+ // Merge the returned value(s) from the handler method to generate the final resposne
143
+ $response = array_merge( $partialResponse, $returnValue );
144
+
145
+ // Return the JSON data.
146
+ echo json_encode( $response );
147
+ die();
148
+ }
149
+
150
+ /**
151
+ * Handles the AJAX request to fetch license information.
152
+ */
153
+ public function handleAjaxFetchLicense() {
154
+ // If not addon ID in the request, stop
155
+ if ( empty( $_GET['addon']) )
156
+ $this->_sendErrorResponse( __( 'No addon ID', WPRSS_TEXT_DOMAIN ) );
157
+ // Get and sanitize the addon ID
158
+ $addon = sanitize_text_field( $_GET['addon'] );
159
+ // Get the license information from EDD
160
+ $response = $this->getManager()->checkLicense( $addon, 'ALL' );
161
+ // Send response as JSON
162
+ echo json_encode( $response );
163
+ die();
164
+ }
165
+
166
+ /**
167
+ * Handles the activation AJAX request to activate the license for the given addon.
168
+ *
169
+ * @param string $addonId The addon ID.
170
+ * @return array The data to add to the AJAX response, containing the license status after activation.
171
+ */
172
+ public function handleAjaxLicenseActivate( $addonId ) {
173
+ $manager = $this->getManager();
174
+ return array(
175
+ 'validity' => $manager->normalizeLicenseApiStatus( $manager->activateLicense( $addonId ) )
176
+ );
177
+ }
178
+
179
+ /**
180
+ * Handles the deactivation AJAX request to deactivate the license for the given addon.
181
+ *
182
+ * @param string $addonId The addon ID.
183
+ * @return array The data to add to the AJAX response, containing the license status after activation.
184
+ */
185
+ public function handleAjaxLicenseDeactivate( $addonId ) {
186
+ $manager = $this->getManager();
187
+ return array(
188
+ 'validity' => $manager->normalizeLicenseApiStatus( $manager->deactivateLicense( $addonId ) )
189
+ );
190
+ }
191
+
192
+ }
includes/Aventura/Wprss/Core/Licensing/License.php ADDED
@@ -0,0 +1,177 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Aventura\Wprss\Core\Licensing;
4
+ use \Aventura\Wprss\Core\Licensing\License\Status;
5
+
6
+ /**
7
+ * This class represents a single license object.
8
+ *
9
+ * IMPORTANT!
10
+ * This version is still untested
11
+ *
12
+ * @version 1.0-alpha
13
+ * @since 4.7.8
14
+ */
15
+ class License {
16
+
17
+ // Default values for license properties
18
+ const KEY_DEFAULT = false;
19
+ const STATUS_DEFAULT = Status::INVALID;
20
+ const EXPIRY_DEFAULT = null;
21
+
22
+ /**
23
+ * License key.
24
+ *
25
+ * @var string
26
+ */
27
+ protected $_key;
28
+
29
+ /**
30
+ * License status.
31
+ *
32
+ * @see Aventura\Wprss\Licensing\License\Status;
33
+ * @var string
34
+ */
35
+ protected $_status;
36
+
37
+ /**
38
+ * License expiry date.
39
+ *
40
+ * @var integer
41
+ */
42
+ protected $_expiry;
43
+
44
+ /** @var string Code of the add-on this license belongs to. */
45
+ protected $_addonCode;
46
+
47
+ /**
48
+ * Constructs a new instance, using the given params or an array of properties if only the first param is given.
49
+ *
50
+ * @param string $key The license key, or an array containing the license data. Default: array()
51
+ * @param string $status The license status. Default: null
52
+ * @param integer $expiry The expiry date of this license. Default: null
53
+ * @see Aventura\Wprss\Licensing\License\Status
54
+ */
55
+ public function __construct( $key = array(), $status = null, $expiry = null, $addonCode = null ) {
56
+ // If first arg is an array,
57
+ if ( is_array( $key ) ) {
58
+ // Get values from the appropriate keys
59
+ $data = array_merge( self::getDefaultSettings(), $key );
60
+ $key = $data['key'];
61
+ $status = $data['status'];
62
+ $expiry = $data['expires'];
63
+ $addonCode = $data['addon_code'];
64
+ }
65
+
66
+ $this
67
+ // Set fields
68
+ ->setKey( $key )
69
+ ->setStatus( $status )
70
+ ->setExpiry( $expiry )
71
+ ->setAddonCode( $addonCode )
72
+ // Call secondary constructor
73
+ ->_construct();
74
+ }
75
+
76
+ /**
77
+ * Internal secondary constructor, for use when class is extended.
78
+ */
79
+ protected function _construct() {}
80
+
81
+ /**
82
+ * Gets the license key.
83
+ *
84
+ * @return string
85
+ */
86
+ public function getKey() {
87
+ return $this->_key;
88
+ }
89
+
90
+ /**
91
+ * Sets the license key.
92
+ *
93
+ * @param string $key The license key.
94
+ * @return self
95
+ */
96
+ public function setKey( $key ) {
97
+ $this->_key = $key;
98
+ return $this;
99
+ }
100
+
101
+ /**
102
+ * Gets the license status.
103
+ *
104
+ * @see Aventura\Wprss\Licensing\License\Status
105
+ * @return string
106
+ */
107
+ public function getStatus() {
108
+ return $this->_status;
109
+ }
110
+
111
+ /**
112
+ * Sets the license status.
113
+ *
114
+ * @see Aventura\Wprss\Licensing\License\Status
115
+ *
116
+ * @param string $status The license status.
117
+ * @return self
118
+ */
119
+ public function setStatus( $status ) {
120
+ $this->_status = $status;
121
+ return $this;
122
+ }
123
+
124
+ /**
125
+ * Gets the license expiry date.
126
+ *
127
+ * @return integer
128
+ */
129
+ public function getExpiry() {
130
+ return $this->_expiry;
131
+ }
132
+
133
+ /**
134
+ * Sets the license expiry date.
135
+ *
136
+ * @param integer $expiry The license expiry date
137
+ */
138
+ public function setExpiry( $expiry ) {
139
+ $this->_expiry = $expiry;
140
+ return $this;
141
+ }
142
+
143
+ /**
144
+ * Set the code of the add-on that this license belongs to.
145
+ *
146
+ * @param string $code Code of the addon that this license belongs to.
147
+ * @return \Aventura\Wprss\Core\Licensing\License This instance.
148
+ */
149
+ public function setAddonCode($code) {
150
+ $this->_addonCode = $code;
151
+ return $this;
152
+ }
153
+
154
+ /**
155
+ * Get the code of the add-on that this license belongs to.
156
+ *
157
+ * @return string Code of the addon that this license belongs to.
158
+ */
159
+ public function getAddonCode() {
160
+ return $this->_addonCode;
161
+ }
162
+
163
+ /**
164
+ * Gets the default values for all properties of the license.
165
+ *
166
+ * @return array
167
+ */
168
+ public static function getDefaultSettings() {
169
+ return array(
170
+ 'key' => '',
171
+ 'status' => Status::INVALID,
172
+ 'expires' => null,
173
+ 'addon_code'=> null,
174
+ );
175
+ }
176
+
177
+ }
includes/Aventura/Wprss/Core/Licensing/License/Status.php ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Aventura\Wprss\Core\Licensing\License;
4
+
5
+ /**
6
+ * Enum-style abstract class for license statuses.
7
+ */
8
+ abstract class Status {
9
+ const VALID = 'valid';
10
+ const INVALID = 'invalid';
11
+ const INACTIVE = 'inactive';
12
+ const EXPIRED = 'expired';
13
+ }
includes/Aventura/Wprss/Core/Licensing/Manager.php ADDED
@@ -0,0 +1,768 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Aventura\Wprss\Core\Licensing;
4
+ use \Aventura\Wprss\Core\Licensing\License\Status;
5
+
6
+ /**
7
+ * Manager class for license handling.
8
+ *
9
+ * @version 1.0
10
+ * @since 4.7.8
11
+ */
12
+ class Manager {
13
+
14
+ // The default updater class
15
+ const DEFAULT_UPDATER_CLASS = '\\Aventura\\Wprss\\Core\\Licensing\\Plugin\\Updater';
16
+
17
+ // Name of license keys option in DB
18
+ const DB_LICENSE_KEYS_OPTION_NAME = 'wprss_settings_license_keys';
19
+ // Name of license statuses option in DB
20
+ const DB_LICENSE_STATUSES_OPTION_NAME = 'wprss_settings_license_statuses';
21
+ // Regex pattern for keys in license option
22
+ const DB_LICENSE_KEYS_OPTION_PATTERN = '%s_license_key';
23
+ // Regex pattern for statuses in license option
24
+ const DB_LICENSE_STATUSES_OPTION_PATTERN = '%s_license_%s';
25
+
26
+ /**
27
+ * License instance.
28
+ *
29
+ * @var array
30
+ */
31
+ protected $_licenses;
32
+
33
+ /**
34
+ * The class to use for updating addons.
35
+ *
36
+ * @var string
37
+ */
38
+ protected $_updaterClass;
39
+
40
+ /**
41
+ * The updater instances for the addons.
42
+ *
43
+ * @var array
44
+ */
45
+ protected $_updaterInstances;
46
+
47
+ protected $_licenseKeysOptionName;
48
+ protected $_licenseStatusesOptionName;
49
+ protected $_expirationNoticePeriod;
50
+ protected $_defaultAuthorName;
51
+
52
+ /**
53
+ * Constructor.
54
+ */
55
+ public function __construct() {
56
+ // Reset the updater instances
57
+ $this->_updaterInstances = array();
58
+ $this->_loadLicenses();
59
+ $this->_construct();
60
+ }
61
+
62
+ /**
63
+ * Internal secondary constructor, used by classes that extend this class.
64
+ */
65
+ protected function _construct() {}
66
+
67
+ /**
68
+ * Gets the name of the class used to update the addons.
69
+ *
70
+ * @return string
71
+ */
72
+ public function getUpdaterClass() {
73
+ if ( is_null($this->_updaterClass) ) {
74
+ return self::DEFAULT_UPDATER_CLASS;
75
+ }
76
+
77
+ return $this->_updaterClass;
78
+ }
79
+
80
+ /**
81
+ * Sets the class used to update the addons.
82
+ *
83
+ * @param string $updaterClass The name of the updater class.
84
+ * @return self
85
+ */
86
+ public function setUpdaterClass( $updaterClass ) {
87
+ $this->_updaterClass = $updaterClass;
88
+ return self;
89
+ }
90
+
91
+
92
+ /**
93
+ * Get the period before license expiration, for which a notice should be displayed.
94
+ *
95
+ * @return int|string An integer, representing the number of secods before license expiry.
96
+ * or a string, representing a time offset that can be used with strtotime().
97
+ */
98
+ public function getExpirationNoticePeriod() {
99
+ $period = $this->_expirationNoticePeriod;
100
+ if ( is_numeric( $period ) ) {
101
+ $period = intval( $period );
102
+ }
103
+
104
+ return $period;
105
+ }
106
+
107
+
108
+ /**
109
+ * Set the period before license expiration, for which a notice should be displayed.
110
+ *
111
+ * @param int|string $period An integer, representing the number of secods before license expiry.
112
+ * @return \Aventura\Wprss\Core\Licensing\Manager This instance.
113
+ */
114
+ public function setExpirationNoticePeriod( $period ) {
115
+ $this->_expirationNoticePeriod = trim($period);
116
+ return $this;
117
+ }
118
+
119
+
120
+ /**
121
+ * Gets the name of the plugin author that is used by default.
122
+ *
123
+ * @return string Nae of the plugin author that is sent to EDD API by default.
124
+ */
125
+ public function getDefaultAuthorName() {
126
+ return $this->_defaultAuthorName;
127
+ }
128
+
129
+
130
+ /**
131
+ * Sets the name of the plugin author that will be used by default.
132
+ *
133
+ * @param string $name Name of the plugin author that will be sent to EDD API by default.
134
+ * @return \Aventura\Wprss\Core\Licensing\Manager This instance.
135
+ */
136
+ public function setDefaultAuthorName($name) {
137
+ $this->_defaultAuthorName = $name;
138
+ return $this;
139
+ }
140
+
141
+
142
+ /**
143
+ * Gets a list of registered addons.
144
+ *
145
+ * @return array An assoc array containing addon IDs as array keys and the addon names as array values.
146
+ */
147
+ public function getAddons() {
148
+ return wprss_get_addons();
149
+ }
150
+
151
+
152
+ /**
153
+ * Gets all of the updater instances.
154
+ *
155
+ * Alias for self#getUpdaterInstance()
156
+ *
157
+ * @see self::getUpdaterInstance
158
+ * @uses self::getUpdaterInstance
159
+ * @return array
160
+ */
161
+ public function getUpdaterInstances() {
162
+ return $this->getUpdaterInstance();
163
+ }
164
+
165
+
166
+ /**
167
+ * Gets the updater instance for a single addon, or all the updater instances if no param or null is passed.#
168
+ *
169
+ * @param mixed $addonId The addon ID for which to return the updater instance, or null to return all instances. Default: null
170
+ * @return mixed An instance of \Aventura\Wprss\Core\Licensing\Plugin\UpdaterInterface if an addon ID is given, or an array of instances is null is given.
171
+ */
172
+ public function getUpdaterInstance( $addonId = null ) {
173
+ return is_null($addonId)
174
+ ? $this->_updaterInstances
175
+ : $this->_getUpdaterInstance($addonId);
176
+ }
177
+
178
+
179
+ /**
180
+ * Internally used to get a single updater instance for a particular addon.
181
+ *
182
+ * @param string $addonId The addon ID.
183
+ * @return \Aventura\Wprss\Core\Licensing\Plugin\UpdaterInterface
184
+ */
185
+ protected function _getUpdaterInstance( $addonId ) {
186
+ return $this->hasUpdaterInsantance($addonId)
187
+ ? $this->_updaterInstances[$addonId]
188
+ : null;
189
+ }
190
+
191
+
192
+ /**
193
+ * Checks if an updater instance exists for a specific addon.
194
+ *
195
+ * @param string $addonId The addon ID.
196
+ * @return boolean True if the updater instance exists, false if not.
197
+ */
198
+ public function hasUpdaterInsantance($addonId) {
199
+ return isset($this->_updaterInstances[$addonId]);
200
+ }
201
+
202
+
203
+ /**
204
+ * Sets the updater instance for an addon.
205
+ *
206
+ * @param string $addonId The addon ID.
207
+ * @param \Aventura\Wprss\Core\Licensing\Plugin\UpdaterInterface The updater instance.
208
+ */
209
+ protected function _setUpdaterInstance($addonId, $instance) {
210
+ $this->_updaterInstances[ $addonId ] = $instance;
211
+ return $this;
212
+ }
213
+
214
+
215
+ /**
216
+ * Creates a new license.
217
+ *
218
+ * This does not save the license to the database. You will need to call Manager::saveLicenses() to save to db.
219
+ *
220
+ * @param string $addonId The addon ID
221
+ * @return License The created license
222
+ */
223
+ public function createLicense( $addonId ) {
224
+ return $this->_licenses[ $addonId ] = new License( array('addon_code' => $addonId) );
225
+ }
226
+
227
+
228
+ /**
229
+ * Gets all license key settings.
230
+ *
231
+ * @return array
232
+ */
233
+ public function getLicenses() {
234
+ return $this->_licenses;
235
+ }
236
+
237
+
238
+ /**
239
+ * Gets the license key for a specific addon.
240
+ *
241
+ * @param string $addonId The addon id.
242
+ * @return array
243
+ */
244
+ public function getLicense( $addonId ) {
245
+ if ( ! $this->licenseExists( $addonId ) ) {
246
+ return null;
247
+ }
248
+ return $this->_licenses[ $addonId ];
249
+ }
250
+
251
+
252
+ /**
253
+ * Checks if an addon license key
254
+ *
255
+ * @param string $addonId The addon id
256
+ * @return boolean True if the license key for the given addon id exists, false if not.
257
+ */
258
+ public function licenseExists( $addonId ) {
259
+ return isset( $this->_licenses[ $addonId ] );
260
+ }
261
+
262
+
263
+ /**
264
+ * Gets all licenses with the given status.
265
+ *
266
+ * @param string $status The status to search for.
267
+ * @param boolean $negation If true, the method will search for licenses that do NOT have the given status.
268
+ * If false, the method will search for licenses with the given status.
269
+ * Default: false
270
+ * @return array An array of matching licenses. Array keys contain the addon IDs and array values contain the license objects.
271
+ */
272
+ public function getLicensesWithStatus( $status, $negation = false ) {
273
+ $licenses = array();
274
+ foreach ( $this->_licenses as $_addonId => $_license ) {
275
+ if ( $_license->getStatus() === $status xor $negation === true ) {
276
+ $licenses[ $_addonId ] = $_license;
277
+ }
278
+ }
279
+ return $licenses;
280
+ }
281
+
282
+
283
+ /**
284
+ * Checks if a license with the given status exists, stopping at the first match.
285
+ *
286
+ * @param string $status The status to search for.
287
+ * @param boolean $negation If true, the method will search for licenses that do NOT have the given status.
288
+ * If false, the method will search for licenses with the given status.
289
+ * Default: false
290
+ * @return boolean True if a license with or without (depending on $negation) the given status exists, false otherwise.
291
+ */
292
+ public function licenseWithStatusExists( $status, $negation = false ) {
293
+ return count( $this->getLicensesWithStatus( $status, $negation ) ) > 0;
294
+ }
295
+
296
+
297
+ /**
298
+ * Gets the licenses that are soon to be expired.
299
+ *
300
+ * @uses self::_calculateSteTimestamp To calculate the minimum date for classifying licenses as "soon-to-expire".
301
+ *
302
+ * @return array An assoc array with addon IDs as array keys and License instances as array values.
303
+ */
304
+ public function getExpiringLicenses() {
305
+ // Calculate soon-to-expiry (ste) date
306
+ $ste = $this->getSteTimestamp();
307
+ // Prepare the list
308
+ $expiringLicences = array();
309
+ // Iterate all licenses
310
+ foreach ( $this->_licenses as $addonId => $license ) {
311
+ // Get expiry
312
+ $expires = $license->getExpiry();
313
+ // Split using space and get first part only (date only)
314
+ $parts = explode( ' ', $expires );
315
+ $dateOnly = strtotime( $parts[0] );
316
+ // Check if the expiry date is zero, or is within the expiration notice period
317
+ if ( $dateOnly == 0 || $dateOnly < $ste ) {
318
+ $expiringLicences[ $addonId ] = $license;
319
+ }
320
+ }
321
+ return $expiringLicences;
322
+ }
323
+
324
+
325
+ /**
326
+ * Checks if there are licenses that will soon expire.
327
+ *
328
+ * @uses self::_calculateSteTimestamp To calculate the minimum date for classifying licenses as "soon-to-expire".
329
+ *
330
+ * @return bool True if there are licenses that will soon expire, false otherwise.
331
+ */
332
+ public function expiringLicensesExist() {
333
+ return count( $this->getExpiringLicenses() ) > 0;
334
+ }
335
+
336
+
337
+ /**
338
+ * Activates an add-on's license.
339
+ *
340
+ * @uses self::sendApiRequest() Sends the request with $action set as 'activate_license'.
341
+ *
342
+ * @param string $addonId The ID of the addon.
343
+ * @param string $return What to return from the response.
344
+ * @return mixed
345
+ */
346
+ public function activateLicense( $addonId, $return = 'license') {
347
+ return $this->sendApiRequest( $addonId, 'activate_license', $return );
348
+ }
349
+
350
+
351
+ /**
352
+ * Deactivates an add-on's license.
353
+ *
354
+ * @uses self::sendApiRequest() Sends the request with $action set as 'deactivate_license'.
355
+ *
356
+ * @param string $addonId The ID of the addon.
357
+ * @param string $return What to return from the response.
358
+ * @return mixed
359
+ */
360
+ public function deactivateLicense( $addonId, $return = 'license') {
361
+ return $this->sendApiRequest( $addonId, 'deactivate_license', $return );
362
+ }
363
+
364
+
365
+ /**
366
+ * Checks an add-on's license's status with the server.
367
+ *
368
+ * @uses self::sendApiRequest() Sends the request with $action set as 'check_license'.
369
+ *
370
+ * @param string $addonId The ID of the addon.
371
+ * @param string $return What to return from the response.
372
+ * @return mixed
373
+ */
374
+ public function checkLicense( $addonId, $return = 'license') {
375
+ return $this->sendApiRequest( $addonId, 'check_license', $return );
376
+ }
377
+
378
+
379
+ /**
380
+ * Calls the EDD Software Licensing API to perform licensing tasks on the addon's store server.
381
+ *
382
+ * @param string $addonId The ID of the addon
383
+ * @param string $action The action to perform on the license.
384
+ * @param string $return What to return from the response. If 'ALL', the entire license status object is returned,
385
+ * Otherwise, the property with name $return will be returned, or null if it doesn't exist.
386
+ * @return mixed
387
+ */
388
+ public function sendApiRequest( $addonId, $action = 'check_license', $return = 'license' ) {
389
+ // Get the license for the addon
390
+ $license = $this->getLicense( $addonId );
391
+ // Use blank license if addon license does not exist
392
+ if ( $license === null ) {
393
+ $license = new License();
394
+ }
395
+
396
+ // Addon Uppercase ID
397
+ $addonUid = strtoupper( $addonId );
398
+ // Prepare constants
399
+ $itemName = constant( "WPRSS_{$addonUid}_SL_ITEM_NAME" );
400
+ $storeUrl = constant( "WPRSS_{$addonUid}_SL_STORE_URL" );
401
+
402
+ try {
403
+ $licenseData = $this->api($storeUrl, array(
404
+ 'edd_action' => $action,
405
+ 'license' => $license,
406
+ 'item_name' => $itemName,
407
+ ));
408
+ } catch ( Exception $e ) {
409
+ wprss_log( sprintf( 'Could not retrieve licensing data from "%1$s": %2$s', $storeUrl, $e->getMessage() ), __FUNCTION__, WPRSS_LOG_LEVEL_WARNING );
410
+ return $license->getStatus();
411
+ }
412
+
413
+ // Update the DB option
414
+ $license->setStatus( $licenseData->license );
415
+ $license->setExpiry( $licenseData->expires );
416
+ $this->saveLicenseStatuses();
417
+
418
+ // Return the data
419
+ if ( strtoupper( $return ) === 'ALL' ) {
420
+ return $licenseData;
421
+ } else {
422
+ return isset( $licenseData->{$return} ) ? $licenseData->{$return} : null;
423
+ }
424
+ }
425
+
426
+
427
+ /**
428
+ * Low-level method for sending API requests to an EDD license server.
429
+ *
430
+ * Will normalize parameters for the request as needed, and return the decoded response.
431
+ *
432
+ * @param string $storeUrl The URL, to which to send the request. The EDD API endpoint.
433
+ * @param array $params Params of the request. 'license', 'edd_action' and
434
+ * 'item_name' are required for a successful call. 'license' can also be
435
+ * an instance of {@link Aventura\Wprss\Core\Licensing\License}.
436
+ * @return object License data as properties of an stdClass instance.
437
+ * @throws Exception If request fails, or returns empty response, or response cannot be decoded.
438
+ */
439
+ public function api($storeUrl, $params) {
440
+ $defaultParams = array(
441
+ 'edd_action' => null,
442
+ 'license' => null,
443
+ 'item_name' => null,
444
+ 'url' => network_site_url(),
445
+ 'time' => time(),
446
+ );
447
+
448
+ // Prepare params
449
+ $params = array_merge($defaultParams, $params);
450
+ array_walk($params, array($this, 'sanitizeApiParams'));
451
+ if ( $params['license'] instanceof License ) $params['license'] = $params['license']->getKey();
452
+ if ( $params['license'] ) $params['license'] = sanitize_text_field ( $params['license']);
453
+ if ( $params['item_name'] ) $params['item_name'] = sanitize_text_field ( $params['item_name']);
454
+ if ( $params['url'] ) $params['url'] = urlencode ( $params['url']);
455
+
456
+ // Send the request to the API
457
+ $response = wp_remote_post( add_query_arg( $params, $storeUrl ) );
458
+
459
+ // Request failed
460
+ if ( is_wp_error( $response ) ) {
461
+ throw new Exception( sprintf( 'Licensing API request failed: %1$s', $response->get_error_message() ) );
462
+ }
463
+
464
+ $body = wp_remote_retrieve_body( $response );
465
+ if ( empty( $body ) ) {
466
+ throw new Exception( sprintf( 'Licensing API response returned empty' ) );
467
+ }
468
+
469
+ if ( ($licenseData = json_decode( $body )) === null ) {
470
+ throw new Exception( sprintf( 'Licensing API response could not be decoded' ) );
471
+ }
472
+
473
+ return $licenseData;
474
+ }
475
+
476
+
477
+ /**
478
+ * Sanitizes the API params, prior to sending them.
479
+ *
480
+ * @param mixed $value The param value
481
+ * @param string $key The param ID
482
+ * @return mixed The sanitized param value.
483
+ */
484
+ public function sanitizeApiParams( &$value, $key ) {
485
+ $value = is_string($value)? urlencode($value) : $value;
486
+ }
487
+
488
+
489
+ /**
490
+ * Sets up the EDD updater for all registered add-ons.
491
+ *
492
+ * @since 4.6.3
493
+ */
494
+ public function initUpdaterInstances() {
495
+ // Stop if doing autosave or ajax
496
+ if ( ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) return;
497
+
498
+ // Get all registered addons
499
+ $addons = $this->getAddons();
500
+
501
+ // Iterate the addons
502
+ foreach( $addons as $id => $name ) {
503
+ // Prepare the data
504
+ $license = $this->getLicense( $id );
505
+ // If the addon doesn't have a license or the license is not valid, skip this addon
506
+ if ( $license === null || $license->getStatus() !== Status::VALID ) continue;
507
+ $uid = strtoupper( $id );
508
+ $name = constant("WPRSS_{$uid}_SL_ITEM_NAME");
509
+ $version = constant("WPRSS_{$uid}_VERSION");
510
+ $path = constant("WPRSS_{$uid}_PATH");
511
+ $storeUrl = defined( "WPRSS_{$uid}_SL_STORE_URL")
512
+ ? constant( "WPRSS_{$uid}_SL_STORE_URL" )
513
+ : WPRSS_SL_STORE_URL;
514
+
515
+ // Set up an updater and register the instance
516
+ $this->_setUpdaterInstance(
517
+ $id,
518
+ $this->newUpdater($storeUrl, $path, array(
519
+ 'version' => $version, // current version number
520
+ 'license' => $license, // license key (used get_option above to retrieve from DB)
521
+ 'item_name' => $name, // name of this plugin
522
+ ))
523
+ );
524
+ }
525
+ }
526
+
527
+
528
+ /**
529
+ * Creates a new instance of the updater class, as configured.
530
+ *
531
+ * Guaranteed to return an updater.
532
+ *
533
+ * @see getUpdaterClass()
534
+ * @see Plugin\UpdaterInterface::__construct();
535
+ * @param string $url Endpoint URL.
536
+ * @param string $path Plugin file.
537
+ * @param array $params Params to requests.
538
+ * @return \Aventura\Wprss\Core\Licensing\Plugin\UpdaterInterface
539
+ */
540
+ public function newUpdater($url, $path, $params = array()) {
541
+ // Get the updater class
542
+ $updaterClass = $this->getUpdaterClass();
543
+
544
+ $defaultParams = array(
545
+ 'author' => $this->getDefaultAuthorName(),
546
+ 'license' => null,
547
+ );
548
+ $params = array_merge($defaultParams, $params);
549
+ if ( $params['license'] instanceof License ) $params['license'] = $params['license']->getKey();
550
+
551
+ $updater = new $updaterClass($url, $path, $params);
552
+ if ( !($updater instanceof Plugin\UpdaterInterface) ) {
553
+ throw new Exception( sprintf( 'Could not create new updater: class "%1$s" is not an updater', get_class( $updater ) ) );
554
+ }
555
+
556
+ return $updater;
557
+ }
558
+
559
+
560
+ /**
561
+ * Normalizes the license status received by the API into the license statuses that we use locally in our code.
562
+ *
563
+ * @param string $status The status to normalize.
564
+ * @return string The normalized status.
565
+ */
566
+ public function normalizeLicenseApiStatus( $status ) {
567
+ if ( $status === 'site_inactive' ) $status = 'inactive';
568
+ if ( $status === 'item_name_mismatch' ) $status = 'invalid';
569
+ return $status;
570
+ }
571
+
572
+
573
+ /**
574
+ * Loads the licenses from db and prepares the internal licenses array
575
+ */
576
+ protected function _loadLicenses() {
577
+ $this->_licenses = array();
578
+ $options = self::_normalizeLicenseOptions( $this->getLicenseKeysDbOption(), $this->getLicenseStatusesDbOption() );
579
+ foreach ( $options as $addonId => $_data ) {
580
+ $this->_licenses[ $addonId ] = new License( $_data );
581
+ }
582
+
583
+ return $this;
584
+ }
585
+
586
+
587
+ /**
588
+ * Saves the licenses and their statuses to the db.
589
+ */
590
+ public function saveLicenses() {
591
+ $this->saveLicenseKeys();
592
+ $this->saveLicenseStatuses();
593
+
594
+ return $this;
595
+ }
596
+
597
+
598
+ /**
599
+ * Saves the license keys to the db.
600
+ */
601
+ public function saveLicenseKeys() {
602
+ $keys = array();
603
+ foreach ( $this->_licenses as $_addonId => $_license ) {
604
+ $_key = sprintf( self::DB_LICENSE_KEYS_OPTION_PATTERN, $_addonId );
605
+ $keys[ $_key ] = $_license->getKey();
606
+ }
607
+ update_option( self::DB_LICENSE_KEYS_OPTION_NAME, $keys );
608
+
609
+ return $this;
610
+ }
611
+
612
+
613
+ /**
614
+ * Saves the license statuses (and expirations) to the db.
615
+ */
616
+ public function saveLicenseStatuses() {
617
+ $statuses = array();
618
+ foreach ( $this->_licenses as $_addonId => $_license ) {
619
+ $_status = sprintf( self::DB_LICENSE_STATUSES_OPTION_PATTERN, $_addonId, 'status' );
620
+ $_expires = sprintf( self::DB_LICENSE_STATUSES_OPTION_PATTERN, $_addonId, 'expires' );
621
+ $statuses[ $_status ] = $_license->getStatus();
622
+ $statuses[ $_expires ] = $_license->getExpiry();
623
+ }
624
+ update_option( self::DB_LICENSE_STATUSES_OPTION_NAME, $statuses );
625
+
626
+ return $this;
627
+ }
628
+
629
+
630
+ /**
631
+ * Retrieves the licenses keys db option.
632
+ *
633
+ * @return array
634
+ */
635
+ public function getLicenseKeysDbOption() {
636
+ return get_option( $this->getLicenseKeysOptionName(), array() );
637
+ }
638
+
639
+
640
+ /**
641
+ * Retrieves the licenses statuses db option.
642
+ *
643
+ * @return array
644
+ */
645
+ public function getLicenseStatusesDbOption() {
646
+ return get_option( $this->getLicenseStatusesOptionName(), array() );
647
+ }
648
+
649
+
650
+ /**
651
+ * @param string $optionName The name of the option where license keys should be stored.
652
+ * @return \Aventura\Wprss\Core\Licensing\Manager This instance.
653
+ */
654
+ public function setLicenseKeysOptionName($optionName) {
655
+ $this->_licenseKeysOptionName = $optionName;
656
+ return $this;
657
+ }
658
+
659
+
660
+ /**
661
+ * @return string The name of the option where license keys are stored.
662
+ */
663
+ public function getLicenseKeysOptionName() {
664
+ if ( is_null( $this->_licenseKeysOptionName ) ) {
665
+ return self::DB_LICENSE_KEYS_OPTION_NAME;
666
+ }
667
+
668
+ return $this->_licenseKeysOptionName;
669
+ }
670
+
671
+
672
+ /**
673
+ * @param string $optionName The name of the option where license statuses should be stored.
674
+ * @return \Aventura\Wprss\Core\Licensing\Manager This instance.
675
+ */
676
+ public function setLicenseStatusesOptionName($optionName) {
677
+ $this->_licenseStatusesOptionName = $optionName;
678
+ return $this;
679
+ }
680
+
681
+
682
+ /**
683
+ * @return string The name of the option where license statuses are stored.
684
+ */
685
+ public function getLicenseStatusesOptionName() {
686
+ if ( is_null( $this->_licenseStatusesOptionName ) ) {
687
+ return self::DB_LICENSE_STATUSES_OPTION_NAME;
688
+ }
689
+
690
+ return $this->_licenseStatusesOptionName;
691
+ }
692
+
693
+
694
+ /**
695
+ * Normalizes the given db options into a format that the Manager can use to compile a list of License instances.
696
+ *
697
+ * @return array
698
+ */
699
+ protected static function _normalizeLicenseOptions( $keys, $statuses ) {
700
+ // Prepare result array
701
+ $normalized = array();
702
+ // Prepare regex pattern outside of iterations
703
+ $licenseKeysOptionPattern = self::_formatStringToDbOptionPattern( self::DB_LICENSE_KEYS_OPTION_PATTERN );
704
+ $licenseStatusesOptionPattern = self::_formatStringToDbOptionPattern( self::DB_LICENSE_STATUSES_OPTION_PATTERN );
705
+
706
+ // Prepare the license keys into the normalized array
707
+ foreach ( $keys as $_key => $_value ) {
708
+ // Regex match for pattern of array keys
709
+ preg_match( $licenseKeysOptionPattern, $_key, $_matches );
710
+ if ( count( $_matches ) < 2 ) continue;
711
+ // Addon id is the first match (excluding whole string match at $_matches[0])
712
+ $_addonId = $_matches[1];
713
+ // check if entry for add-on exists in normalized array, otherwise create it
714
+ if ( ! isset( $normalized[ $_addonId ] ) )
715
+ $normalized[ $_addonId ] = array();
716
+ // Insert the license key inot the normalized array
717
+ $normalized[ $_addonId ]['key'] = $_value;
718
+ $normalized[ $_addonId ]['addon_code'] = $_addonId;
719
+ }
720
+ // Now iterate and insert the statuses
721
+ foreach ( $statuses as $_key => $_value ) {
722
+ // Regex match for pattern of array keys
723
+ preg_match( $licenseStatusesOptionPattern, $_key, $_matches );
724
+ // continue to next iteration if there are no matches
725
+ if ( count( $_matches ) < 3 ) continue;
726
+ // The addon ID: first match
727
+ $_addonId = $_matches[1];
728
+ // Property name: second match
729
+ $_property = $_matches[2];
730
+ // if entry for add-on doesn't exist in normalized array, continue
731
+ if ( ! isset( $normalized[ $_addonId ] ) ) continue;
732
+ // Add the property to the normalized array for the addon's entry
733
+ $normalized[ $_addonId ][ $_property ] = $_value;
734
+ }
735
+
736
+ return $normalized;
737
+ }
738
+
739
+
740
+ /**
741
+ * Converts the given format string into a regex pattern, replacing all instances of '%s' with
742
+ * '([^_]+)'. The pattern can be used by PHP regex functions to match db license options.
743
+ *
744
+ * @param string $formatString
745
+ * @return string
746
+ */
747
+ protected static function _formatStringToDbOptionPattern( $formatString ) {
748
+ return sprintf( '/\\A%s\\Z/', str_replace( '%s', '([^_\s]+)', $formatString ) );
749
+ }
750
+
751
+
752
+ /**
753
+ * Calculates the "soon-to-expire" timestamp.
754
+ *
755
+ * @return integer The timestamp for a future date, for which addons whose license's expiry lies between this date and the present are considered "soon-to-expire".
756
+ */
757
+ public function getSteTimestamp() {
758
+ $period = $this->getExpirationNoticePeriod();
759
+ if ( is_null( $period ) ) {
760
+ return null;
761
+ }
762
+
763
+ return is_numeric( $period )
764
+ ? time() + $period
765
+ : strtotime( sprintf( '+%s', $period ) );
766
+ }
767
+
768
+ }
includes/Aventura/Wprss/Core/Licensing/Plugin/Updater.php ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Aventura\Wprss\Core\Licensing\Plugin;
4
+
5
+ // Load Easy Digital Downloads - Software Licensing updater class file
6
+ if ( ! class_exists('EDD_SL_Plugin_Updater') ) {
7
+ require_once( WPRSS_INC . 'libraries/EDD_licensing/EDD_SL_Plugin_Updater.php' );
8
+ }
9
+
10
+ /**
11
+ * Updater class, extending the Software Licensing updater and implementing the updater interface.
12
+ */
13
+ class Updater extends \EDD_SL_Plugin_Updater implements UpdaterInterface {
14
+
15
+ }
includes/Aventura/Wprss/Core/Licensing/Plugin/UpdaterInterface.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Aventura\Wprss\Core\Licensing\Plugin;
4
+
5
+ /**
6
+ * Interface for a plugin updater class.
7
+ */
8
+ interface UpdaterInterface {
9
+
10
+ /**
11
+ * @param type $apiUrl The URL pointing to the custom API endpoint.
12
+ * @param type $pluginFile Path to the plugin file.
13
+ * @param type $apiData Optional data to send with API calls.
14
+ */
15
+ public function __construct($apiUrl, $pluginFile, $apiData = array());
16
+ }
includes/Aventura/Wprss/Core/Licensing/Settings.php ADDED
@@ -0,0 +1,382 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Aventura\Wprss\Core\Licensing;
4
+ use \Aventura\Wprss\Core\Licensing\License\Status;
5
+ use \WPRSS_MBString;
6
+
7
+ /**
8
+ * The licensing settings class.
9
+ *
10
+ * Manages registraion, rendering and validation of the licensing settings as well as handling AJAX requests
11
+ * from the client. This class uses the Manager class internally to retrieve, update and manage licenses.
12
+ */
13
+ class Settings {
14
+
15
+ /**
16
+ * What to print in place of license code chars.
17
+ * This must not be a symbol that is considered to be a valid license key char.
18
+ *
19
+ * @since 4.6.10
20
+ */
21
+ const LICENSE_KEY_MASK_CHAR = '•';
22
+
23
+ /**
24
+ * How many characters of the license code to print as is.
25
+ * Use negative value to indicate that characters at the end of the key are excluded.
26
+ *
27
+ * @since 4.6.10
28
+ */
29
+ const LICENSE_KEY_MASK_EXCLUDE_AMOUNT = -4;
30
+
31
+ /**
32
+ * The Licensing Manager instance.
33
+ *
34
+ * @var Manager
35
+ */
36
+ protected $_manager;
37
+
38
+ /**
39
+ * Constructor.
40
+ */
41
+ public function __construct() {
42
+ $this->_setManager( wprss_licensing_get_manager() );
43
+ // Only load notices if on admin side
44
+ if ( is_admin() ) {
45
+ $this->_initNotices();
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Sets the license manager for this settings controller to use.
51
+ *
52
+ * @param \Aventura\Wprss\Core\Licensing\Manager $manager An instance of the license manager.
53
+ * @return \Aventura\Wprss\Core\Licensing\Settings This instance.
54
+ */
55
+ protected function _setManager(Manager $manager) {
56
+ $this->_manager = $manager;
57
+ return $this;
58
+ }
59
+
60
+ /**
61
+ * Gets the license manager for this settings controller.
62
+ *
63
+ * @return \Aventura\Wprss\Core\Licensing\Manager The license manager used by this settings controller.
64
+ */
65
+ public function getManager() {
66
+ return $this->_manager;
67
+ }
68
+
69
+ /**
70
+ * Initializes the admin notices.
71
+ *
72
+ * @return \Aventura\Wprss\Core\Licensing\Settings
73
+ */
74
+ protected function _initNotices() {
75
+ $noticesCollection = wprss_admin_notice_get_collection();
76
+ foreach ( $this->getManager()->getAddons() as $_addonId => $_addonName ) {
77
+ $_notice = array(
78
+ 'id' => sprintf( 'invalid_licenses_exist_%s', $_addonId ),
79
+ 'notice_type' => 'error',
80
+ 'content' => $this->getInvalidLicenseNoticeContent( $_addonId ),
81
+ 'condition' => array( array( $this, 'invalidLicensesNoticeCondition' ) ),
82
+ 'addon' => $_addonId
83
+ );
84
+ $noticesCollection->add_notice( $_notice );
85
+ }
86
+
87
+ return $this;
88
+ }
89
+
90
+ /**
91
+ * Condition callback for the "invalid license notice".
92
+ *
93
+ * @return boolean True if the notice is to be shown, false if not.
94
+ */
95
+ public function invalidLicensesNoticeCondition( $args ) {
96
+ if ( isset( $args['addon'] ) ) return false;
97
+ $license = $this->getManager()->getLicense( $args['addon'] );
98
+ return $license !== null && $license->getStatus() !== Status::VALID;
99
+ }
100
+
101
+ /**
102
+ * Gets the content of the notice that informs the user of invalid licenses.
103
+ *
104
+ * @param string $addonId The ID of addon that has the invalid license.
105
+ * @return string
106
+ */
107
+ public function getInvalidLicenseNoticeContent( $addonId ) {
108
+ $addons = $this->getManager()->getAddons();
109
+ $addonName = $addons[ $addonId ];
110
+ return sprintf(
111
+ __( '<p>Remember to <a href="%s">enter your plugin license code</a> for the WP RSS Aggregator <strong>%s</strong> add-on to benefit from updates and support.</p>', WPRSS_TEXT_DOMAIN ),
112
+ esc_attr( admin_url( 'edit.php?post_type=wprss_feed&page=wprss-aggregator-settings&tab=licenses_settings' ) ),
113
+ $addonName
114
+ );
115
+ }
116
+
117
+ /**
118
+ * Registers the WordPress settings.
119
+ */
120
+ public function registerSettings() {
121
+ // Iterate all addon IDs and register a settings section with 2 fields for each.
122
+ foreach( $this->getManager()->getAddons() as $_addonId => $_addonName ) {
123
+ // Settings Section
124
+ add_settings_section(
125
+ sprintf( 'wprss_settings_%s_licenses_section', $_addonId ),
126
+ sprintf( '%s %s', $_addonName, __( 'License', WPRSS_TEXT_DOMAIN ) ),
127
+ '__return_empty_string',
128
+ 'wprss_settings_license_keys'
129
+ );
130
+ // License key field
131
+ add_settings_field(
132
+ sprintf( 'wprss_settings_%s_license', $_addonId ),
133
+ __( 'License Key', WPRSS_TEXT_DOMAIN ),
134
+ array( $this, 'renderLicenseKeyField' ),
135
+ 'wprss_settings_license_keys',
136
+ sprintf( 'wprss_settings_%s_licenses_section', $_addonId ),
137
+ array( $_addonId )
138
+ );
139
+ // Activate license button
140
+ add_settings_field(
141
+ sprintf( 'wprss_settings_%s_activate_license', $_addonId ),
142
+ __( 'Activate License', WPRSS_TEXT_DOMAIN ),
143
+ array( $this, 'renderActivateLicenseButton' ),
144
+ 'wprss_settings_license_keys',
145
+ sprintf( 'wprss_settings_%s_licenses_section', $_addonId ),
146
+ array( $_addonId )
147
+ );
148
+ }
149
+
150
+ return $this;
151
+ }
152
+
153
+ /**
154
+ * Renders the license field for a particular add-on.
155
+ *
156
+ * @since 4.4.5
157
+ */
158
+ public function renderLicenseKeyField( $args ) {
159
+ if ( count( $args ) < 1 ) return;
160
+ // Addon ID is the first arg
161
+ $addonId = $args[0];
162
+ // Get the addon's license
163
+ $license = $this->getManager()->getLicense( $addonId );
164
+ // Mask it - if the license exists
165
+ $displayedKey = is_null( $license )? '' : self::obfuscateLicenseKey( $license->getKey() );
166
+ // Render the markup ?>
167
+ <input id="wprss-<?php echo $addonId ?>-license-key" name="wprss_settings_license_keys[<?php echo $addonId ?>_license_key]"
168
+ type="text" value="<?php echo esc_attr( $displayedKey ) ?>" style="width: 300px;"
169
+ />
170
+ <label class="description" for="wprss-<?php echo $addonId ?>-license-key">
171
+ <?php _e( 'Enter your license key', WPRSS_TEXT_DOMAIN ) ?>
172
+ </label><?php
173
+ }
174
+
175
+
176
+ /**
177
+ * Masks a license key.
178
+ *
179
+ * @param string $licenseKey The license key to mask
180
+ * @param string $maskChar The masking character(s)
181
+ * @param integer $maskExcludeAmount The amount of characyers to exclude from the mask. If negative, the exluded characters will begin from the end of the string
182
+ * @return string The masked license key
183
+ */
184
+ public static function obfuscateLicenseKey( $licenseKey, $maskChar = self::LICENSE_KEY_MASK_CHAR, $maskExcludeAmount = self::LICENSE_KEY_MASK_EXCLUDE_AMOUNT ) {
185
+ // Pre-calculate license key length
186
+ $licenseKeyLength = strlen( $licenseKey );
187
+ // In case the mask exclude amount is greater than the license key length
188
+ $actualMaskExcludeAmount = abs( $maskExcludeAmount ) > ( $licenseKeyLength - 1 )
189
+ ? ( $licenseKeyLength - 1 ) * ( $maskExcludeAmount < 0 ? -1 : 1 ) // Making sure to preserve position of mask
190
+ : $maskExcludeAmount;
191
+ // How many chars to mask. Always at least one char will be masked.
192
+ $maskLength = $licenseKeyLength - abs( $actualMaskExcludeAmount );
193
+ // Create the mask
194
+ $mask = $maskLength > 0 ? str_repeat( $maskChar, $maskLength ) : '';
195
+ // The starting index: if negative mask exclude amount, start from the back. otherwise start from 0
196
+ $startIndex = $actualMaskExcludeAmount < 0 ? $maskLength : 0;
197
+ // Extract the excluded characters
198
+ $excludedChars = WPRSS_MBString::mb_substr( $licenseKey, $startIndex, abs( $actualMaskExcludeAmount ) );
199
+ // Generate the displayed key and return it
200
+ return sprintf( $actualMaskExcludeAmount > 0 ? '%1$s%2$s' : '%2$s%1$s', $excludedChars, $mask );
201
+ }
202
+
203
+ /**
204
+ * Determines whether or not the license key in question is obfuscated.
205
+ *
206
+ * This is achieved by searching for the mask character in the key. Because the
207
+ * mask character cannot be a valid license character, the presence of at least
208
+ * one such character indicates that the key is obfuscated.
209
+ *
210
+ * @param string $key The license key in question.
211
+ * @param string $maskChar The masking character(s).
212
+ * @return bool Whether or not this key is obfuscated.
213
+ */
214
+ public function isLicenseKeyObfuscated( $key, $maskChar = self::LICENSE_KEY_MASK_CHAR ) {
215
+ return WPRSS_MBString::mb_strpos( $key, $maskChar ) !== false;
216
+ }
217
+
218
+
219
+ /**
220
+ * Invalidates the key if it is obfuscated, causing the saved version to be used.
221
+ * This meanst that the new key will not be saved, as it is considered then to be unchanged.
222
+ *
223
+ * @since 4.6.10
224
+ * @param bool $is_valid Indicates whether the key is currently considered to be valid.
225
+ * @param string $key The license key in question
226
+ * @return Whether or not the key is still to be considered valid.
227
+ */
228
+ public function validateLicenseKeyForSave( $is_valid, $key ) {
229
+ if ( $this->isLicenseKeyObfuscated( $key ) )
230
+ return false;
231
+
232
+ return $is_valid;
233
+ }
234
+
235
+ /**
236
+ * Renders the activate/deactivate license button for a particular add-on.
237
+ *
238
+ * @since 4.4.5
239
+ */
240
+ public function renderActivateLicenseButton( $args ) {
241
+ $addonId = $args[0];
242
+ $manager = $this->getManager();
243
+ $data = $manager->checkLicense( $addonId, 'ALL' );
244
+ $status = is_string( $data ) ? $data : $data->license;
245
+ if ( $status === 'site_inactive' ) $status = 'inactive';
246
+ if ( $status === 'item_name_mismatch' ) $status = 'invalid';
247
+
248
+ $valid = $status == 'valid';
249
+ $btnText = $valid ? 'Deactivate License' : 'Activate License';
250
+ $btnName = "wprss_{$addonId}_license_" . ( $valid? 'deactivate' : 'activate' );
251
+ $btnClass = "button-" . ( $valid ? 'deactivate' : 'activate' ) . "-license";
252
+ wp_nonce_field( "wprss_{$addonId}_license_nonce", "wprss_{$addonId}_license_nonce", false ); ?>
253
+
254
+ <input type="button" class="<?php echo $btnClass; ?> button-process-license button-secondary" name="<?php echo $btnName; ?>" value="<?php _e( $btnText, WPRSS_TEXT_DOMAIN ); ?>" />
255
+ <span id="wprss-<?php echo $addonId; ?>-license-status-text">
256
+ <strong><?php _e('Status', WPRSS_TEXT_DOMAIN); ?>:
257
+ <span class="wprss-<?php echo $addonId; ?>-license-<?php echo $status; ?>">
258
+ <?php _e( ucfirst($status), WPRSS_TEXT_DOMAIN ); ?>
259
+ <?php if ( $status === 'valid' ) : ?>
260
+ <i class="fa fa-check"></i>
261
+ <?php elseif( $status === 'invalid' || $status === 'expired' ): ?>
262
+ <i class="fa fa-times"></i>
263
+ <?php elseif( $status === 'inactive' ): ?>
264
+ <i class="fa fa-warning"></i>
265
+ <?php endif; ?>
266
+ </strong>
267
+ </span>
268
+ </span>
269
+
270
+ <p>
271
+ <?php
272
+ $license = $manager->getLicense( $addonId );
273
+ if ( $license !== null && ($licenseKey = $license->getKey()) && !empty( $licenseKey ) ) :
274
+ if ( is_object( $data ) ) :
275
+ $currentActivations = $data->site_count;
276
+ $activationsLeft = $data->activations_left;
277
+ $activationsLimit = $data->license_limit;
278
+ $expires = $data->expires;
279
+ $expires = substr( $expires, 0, strpos( $expires, " " ) );
280
+
281
+ // If the license key is garbage, don't show any of the data.
282
+ if ( !empty($data->payment_id) && !empty($data->license_limit ) ) :
283
+ ?>
284
+ <small>
285
+ <?php if ( $status !== 'valid' && $activationsLeft === 0 ) : ?>
286
+ <?php $accountUrl = 'https://www.wprssaggregator.com/account/?action=manage_licenses&payment_id=' . $data->payment_id; ?>
287
+ <a href="<?php echo $accountUrl; ?>"><?php _e("No activations left. Click here to manage the sites you've activated licenses on.", WPRSS_TEXT_DOMAIN); ?></a>
288
+ <br/>
289
+ <?php endif; ?>
290
+ <?php if ( strtotime($expires) < strtotime("+2 weeks") ) : ?>
291
+ <?php $renewalUrl = esc_attr(WPRSS_SL_STORE_URL . '/checkout/?edd_license_key=' . $licenseKey); ?>
292
+ <a href="<?php echo $renewalUrl; ?>"><?php _e('Renew your license to continue receiving updates and support.', WPRSS_TEXT_DOMAIN); ?></a>
293
+ <br/>
294
+ <?php endif; ?>
295
+ <strong><?php _e('Activations', WPRSS_TEXT_DOMAIN); ?>:</strong>
296
+ <?php echo $currentActivations.'/'.$activationsLimit; ?> (<?php echo $activationsLeft; ?> left)
297
+ <br/>
298
+ <strong><?php _e('Expires on', WPRSS_TEXT_DOMAIN); ?>:</strong>
299
+ <code><?php echo $expires; ?></code>
300
+ <br/>
301
+ <strong><?php _e('Registered to', WPRSS_TEXT_DOMAIN); ?>:</strong>
302
+ <?php echo $data->customer_name; ?> (<code><?php echo $data->customer_email; ?></code>)
303
+ </small>
304
+ <?php endif; ?>
305
+ <?php else: ?>
306
+ <small><?php _e('Failed to get license information. This is a temporary problem. Check your internet connection and try again later.', WPRSS_TEXT_DOMAIN); ?></small>
307
+ <?php endif; ?>
308
+ <?php endif;
309
+ ?>
310
+ </p>
311
+
312
+ <style type="text/css">
313
+ .wprss-<?php echo $addonId; ?>-license-valid {
314
+ color: green;
315
+ }
316
+ .wprss-<?php echo $addonId; ?>-license-invalid, .wprss-<?php echo $addonId; ?>-license-expired {
317
+ color: #b71919;
318
+ }
319
+ .wprss-<?php echo $addonId; ?>-license-inactive {
320
+ color: #d19e5b;
321
+ }
322
+ #wprss-<?php echo $addonId; ?>-license-status-text {
323
+ margin-left: 8px;
324
+ line-height: 27px;
325
+ vertical-align: middle;
326
+ }
327
+ </style>
328
+ <?php
329
+ }
330
+
331
+ /**
332
+ * Handles the activation/deactivation process
333
+ *
334
+ * @since 1.0
335
+ */
336
+ public function handleLicenseStatusChange() {
337
+ $manager = $this->getManager();
338
+ $addons = $manager->getAddons();
339
+
340
+ // Get for each registered addon
341
+ foreach( $addons as $id => $name ) {
342
+ // listen for our activate button to be clicked
343
+ if( isset( $_POST["wprss_{$id}_license_activate"] ) || isset( $_POST["wprss_{$id}_license_deactivate"] ) ) {
344
+ // run a quick security check
345
+ if ( ! check_admin_referer( "wprss_{$id}_license_nonce", "wprss_{$id}_license_nonce" ) )
346
+ continue; // get out if we didn't click the Activate/Deactivate button
347
+ }
348
+
349
+ // retrieve the license
350
+ $license = $manager->getLicense( $id );
351
+
352
+ // If the license is not saved in DB, but is included in POST
353
+ if ( $license == '' && ! empty( $_POST['wprss_settings_license_keys'][$id.'_license_key'] ) ) {
354
+ // Use the license given in POST
355
+ $license->setKey( $_POST['wprss_settings_license_keys'][ $id.'_license_key' ] );
356
+ }
357
+
358
+ // Prepare the action to take
359
+ if ( isset( $_POST["wprss_{$id}_license_activate"] ) ) {
360
+ $manager->activateLicense( $id );
361
+ }
362
+ elseif ( isset( $_POST["wprss_{$id}_license_deactivate"] ) ) {
363
+ $manager->deactivateLicense( $id );
364
+ }
365
+ }
366
+
367
+ return $this;
368
+ }
369
+
370
+ /**
371
+ * Gets the HTML markup for the activate license button.
372
+ *
373
+ * @param string $addonId The ID of the addon for which the button will be related to.
374
+ * @return string The HTML markup of the button.
375
+ */
376
+ public function getActivateLicenseButtonHtml( $addonId ) {
377
+ ob_start();
378
+ $this->renderActivateLicenseButton( array( $addonId ) );
379
+ return ob_get_clean();
380
+ }
381
+
382
+ }
includes/Aventura/Wprss/Core/Loader.php ADDED
@@ -0,0 +1,285 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ namespace Aventura\Wprss\Core;
4
+
5
+ /**
6
+ * Responsible for autoloading classes.
7
+ */
8
+ class Loader {
9
+
10
+ const WHITESPACE_CHARS = " \t\n\r\0\x0B";
11
+
12
+ protected $_psr4 = array();
13
+ protected $_isRegistered = false;
14
+ protected $_ext = array();
15
+
16
+ public function __construct() {
17
+ $this->_construct();
18
+ }
19
+
20
+ protected function _construct() {
21
+ $this->addExtension('php');
22
+ }
23
+
24
+ /**
25
+ * Registers this instance for handling autoloading, if not already registered.
26
+ *
27
+ * @return \Aventura\Wprss\Core\Loader This instance.
28
+ */
29
+ public function register() {
30
+ if (!$this->isRegistered()) {
31
+ $this->_register();
32
+ }
33
+
34
+ return $this;
35
+ }
36
+
37
+ /**
38
+ * Low level method for registering the autoload function.
39
+ *
40
+ * No checks performed.
41
+ *
42
+ * @see spl_autoload_register()
43
+ * @return \Aventura\Wprss\Core\Loader This instance.
44
+ */
45
+ protected function _register() {
46
+ spl_autoload_register(array($this, 'autoload'));
47
+ return $this;
48
+ }
49
+
50
+ /**
51
+ * Checks whether or not this class is already registered for handling autoloading.
52
+ *
53
+ * @see register()
54
+ * @return boolean True if already registered; false otherwise.
55
+ */
56
+ public function isRegistered() {
57
+ return (bool)$this->_isRegistered;
58
+ }
59
+
60
+ /**
61
+ * Implementation of an autoloading function.
62
+ *
63
+ * @see spl_autoload()
64
+ * @param string $class Name of the class to load.
65
+ */
66
+ public function autoload($class) {
67
+ if (!($basePath = $this->match($class))) {
68
+ return;
69
+ }
70
+
71
+ $relativePath = $this->getClassRelativePath($class);
72
+ if ($path = $this->findClassFile($relativePath, $basePath)) {
73
+ include_once($path);
74
+ }
75
+ }
76
+
77
+ /**
78
+ * Alias of addPsr4().
79
+ *
80
+ * @see addPsr4
81
+ * @return \Aventura\Wprss\Core\Loader This instance.
82
+ */
83
+ public function add($namespace, $basePath) {
84
+ $this->addPsr4($namespace, $basePath);
85
+ return $this;
86
+ }
87
+
88
+ /**
89
+ * Tells the loader to load classes in specified namespace from specified directory.
90
+ *
91
+ * This is according to the PSR-4 standard.
92
+ *
93
+ * @param string $namespace The namespace, files for which should be loaded from the path.
94
+ * @param string $basePath Path to the directory, which is the base directory for class files.
95
+ * @return \Aventura\Wprss\Core\Loader This instance.
96
+ */
97
+ public function addPsr4($namespace, $basePath) {
98
+ $namespace = $this->normalizeClassName($namespace);
99
+ $basePath = $this->normalizeBasePath($basePath);
100
+
101
+ $this->_psr4[$namespace] = $basePath;
102
+ return $this;
103
+ }
104
+
105
+ /**
106
+ * Matches the class name with all registered namespaces.
107
+ *
108
+ * Used to find the base path, where the class should be loaded from.
109
+ *
110
+ * @param string $class Name of the class to find the base path for.
111
+ * @return string|null The basepath that corresponds to the registered namespace of the class, if such a namespace is found.
112
+ * Otherwise, null.
113
+ */
114
+ public function match($class) {
115
+ if ($basePath = $this->matchPsr4($class)) {
116
+ return $basePath;
117
+ }
118
+
119
+ return null;
120
+ }
121
+
122
+ /**
123
+ * Matches the class agains the registered PSR-4 namespaces.
124
+ *
125
+ * @see match()
126
+ * @param string $class Class name to match.
127
+ * @return string|null Basepath for PSR-4 namespace, or null if no match.
128
+ */
129
+ public function matchPsr4($class) {
130
+ foreach ($this->getPsr4Namespaces() as $_ns => $_basePath) {
131
+ if ( $this->matchNamespace( $_ns, $class ) ) {
132
+ return $_basePath;
133
+ }
134
+ }
135
+
136
+ return null;
137
+ }
138
+
139
+ /**
140
+ * Get all registered PSR-4 namespaces.
141
+ *
142
+ * They will be sorted by longest first. This allows to avoid instances, where
143
+ * class `My\Namespace\Cool\Class` will be loaded from the basepath that
144
+ * corresponds to `My\Namespace` even if `My\Namespace\Cool` is registered.
145
+ *
146
+ * @see sortNamespaces()
147
+ * @return array Array of registered PSR-4 namespaces, sorted by longest first.
148
+ */
149
+ public function getPsr4Namespaces() {
150
+ return $this->sortNamespaces($this->_psr4);
151
+ }
152
+
153
+ /**
154
+ * Sorts an array of strings by longest first.
155
+ *
156
+ * @param array $namespaces The array to sort.
157
+ * @return array Array of strings, sorted by longest first.
158
+ */
159
+ public function sortNamespaces($namespaces) {
160
+ uasort($namespaces, function($a, $b) {
161
+ return strlen($b) - strlen($a);
162
+ });
163
+
164
+ return $namespaces;
165
+ }
166
+
167
+ /**
168
+ * Determines whether a class belongs to a namespace.
169
+ *
170
+ * @param string $namespace Namespace to match the class against.
171
+ * @param string $class Name of the class to match.
172
+ * @return boolean True if the specified class belongs to the specified namespace.
173
+ */
174
+ public function matchNamespace($namespace, $class) {
175
+ $namespace = $this->normalizeClassName($namespace);
176
+ $class = $this->normalizeClassName($class);
177
+ return strpos( $class, $namespace ) === 0;
178
+ }
179
+
180
+ /**
181
+ * Normalizes a class name.
182
+ *
183
+ * @param string $className Class name to normalize.
184
+ * @return string The class name with whitespace and namespace separators removed from beginning and end.
185
+ */
186
+ public function normalizeClassName($className) {
187
+ return trim($className, self::WHITESPACE_CHARS . '\\');
188
+ }
189
+
190
+ /**
191
+ * Normalizes a path in a way that is independent of platform's directory separator.
192
+ *
193
+ * @param string $path Path to normalize.
194
+ * @return string The path with whitespace and directory separators removed from end.
195
+ * Whitespace will be removed from beginning as well.
196
+ */
197
+ public function normalizeBasePath($path) {
198
+ $path = ltrim($path);
199
+ return rtrim($path, self::WHITESPACE_CHARS . '\\/' . DIRECTORY_SEPARATOR);
200
+ }
201
+
202
+ /**
203
+ * Normalizes a file extension.
204
+ *
205
+ * @param string $extension The extension to normalize.
206
+ * @return string The file extension with whitespace and period trimmed.
207
+ */
208
+ public function normalizeExtension($extension) {
209
+ return trim($extension, self::WHITESPACE_CHARS . '.');
210
+ }
211
+
212
+ /**
213
+ * Deduces the relative path to the file of a PSR-0 or PSR-4 class.
214
+ *
215
+ * @param string $class Name of the class.
216
+ * @return string The relative path deduced from the class name, without extension.
217
+ */
218
+ public function getClassRelativePath($class) {
219
+ $class = $this->normalizeClassName($class);
220
+ $path = str_replace(array('\\', '_'), DIRECTORY_SEPARATOR, $class);
221
+
222
+ return $path;
223
+ }
224
+
225
+ /**
226
+ * Looks for a file with all registered extensions in all specified paths.
227
+ *
228
+ * @see getClassRelativePath()
229
+ * @param string $path Relative path to a class file, without extension.
230
+ * @param string|array $basePath Path or array of paths to the base directory in which to search for the file.
231
+ * @return string|null Path to the class file, if found; otherwise, null.
232
+ */
233
+ public function findClassFile($path, $basePath) {
234
+ $basePath = (array)$basePath;
235
+ $extensions = $this->getAllowedExtensions();
236
+ foreach ($basePath as $_path) {
237
+ foreach ($extensions as $_ext) {
238
+ $classPath = implode(DIRECTORY_SEPARATOR, array($_path, "{$path}.{$_ext}"));
239
+ if (file_exists($classPath)) {
240
+ return $classPath;
241
+ }
242
+ }
243
+ }
244
+
245
+ return null;
246
+ }
247
+
248
+ /**
249
+ * Retrieves the list of all extensions, for which this loader will autoload class files.
250
+ *
251
+ * @return array Array of registered extensions.
252
+ */
253
+ public function getAllowedExtensions() {
254
+ return array_keys($this->_ext);
255
+ }
256
+
257
+ /**
258
+ * Allows an extension for class files.
259
+ *
260
+ * @param string $extension Extension to allow.
261
+ * @return \Aventura\Wprss\Core\Loader This instance.
262
+ *
263
+ */
264
+ public function addExtension($extension) {
265
+ $extension = $this->normalizeExtension($extension);
266
+ $this->_ext[$extension] = true;
267
+
268
+ return $this;
269
+ }
270
+
271
+ /**
272
+ * Disallows an extension for class files.
273
+ *
274
+ * @param string $extension Extension to disallow.
275
+ * @return \Aventura\Wprss\Core\Loader This instance.
276
+ */
277
+ public function removeExtension($extension) {
278
+ $extension = $this->normalizeExtension($extension);
279
+ if (isset($this->_ext[$extension])) {
280
+ unset($this->_ext[$extension]);
281
+ }
282
+
283
+ return $this;
284
+ }
285
+ }
includes/admin-ajax-notice.php CHANGED
@@ -1127,8 +1127,7 @@ add_action( 'init', 'wprss_admin_notice_get_collection', 9 );
1127
  * @since 4.7.4
1128
  * @uses-filter wprss_admin_notice_collection_before_init To modify collection before initialization.
1129
  * @uses-filter wprss_admin_notice_collection_after_init To modify collection after initialization.
1130
- * @uses-filter wprss_admin_notice_collection_before_enqueue_scripts To modify list of script handles to enqueue.
1131
- * @uses-action wprss_admin_notice_collection_after_enqueue_scripts To access list of enqueued script handles.
1132
  * @uses-filter wprss_admin_notice_collection_before_localize_vars To modify list of vars to expose to the frontend.
1133
  * @uses-action wprss_admin_notice_collection_after_localize_vars To access list of vars exposed to the frontend.
1134
  * @staticvar WPRSS_Admin_Notices $collection The singleton instance.
@@ -1148,24 +1147,40 @@ function wprss_admin_notice_get_collection() {
1148
  $collection->init();
1149
  $collection = apply_filters( 'wprss_admin_notice_collection_after_init', $collection );
1150
 
1151
- $script_handles = apply_filters( 'wprss_admin_notice_collection_before_enqueue_scripts', array( 'wprss-admin-notifications' ), $collection );
1152
- foreach ( $script_handles as $_idx => $_handle ) wp_enqueue_script( $_handle );
1153
- do_action( 'wprss_admin_notice_collection_after_enqueue_scripts', $script_handles, $collection );
1154
-
1155
- // Frontend settings
1156
- $settings = apply_filters( 'wprss_admin_notice_collection_before_localize_vars', array(
1157
- 'notice_class' => $collection->get_notice_base_class(),
1158
- 'nonce_class' => $collection->get_nonce_base_class(),
1159
- 'btn_close_class' => $collection->get_btn_close_base_class(),
1160
- 'action_code' => wprss_admin_notice_get_action_code()
1161
- ), $collection );
1162
- wp_localize_script( 'aventura', 'adminNoticeGlobalVars', $settings);
1163
- do_action( 'wprss_admin_notice_collection_after_localize_vars', $settings, $collection );
1164
  }
1165
 
1166
  return $collection;
1167
  }
1168
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1169
 
1170
  /**
1171
  * Centralizes access to the name of the AJAX action handler for dismissing admin notices.
1127
  * @since 4.7.4
1128
  * @uses-filter wprss_admin_notice_collection_before_init To modify collection before initialization.
1129
  * @uses-filter wprss_admin_notice_collection_after_init To modify collection after initialization.
1130
+ * @uses-action admin_enqueue_scripts To enqueue the scripts for the collection.
 
1131
  * @uses-filter wprss_admin_notice_collection_before_localize_vars To modify list of vars to expose to the frontend.
1132
  * @uses-action wprss_admin_notice_collection_after_localize_vars To access list of vars exposed to the frontend.
1133
  * @staticvar WPRSS_Admin_Notices $collection The singleton instance.
1147
  $collection->init();
1148
  $collection = apply_filters( 'wprss_admin_notice_collection_after_init', $collection );
1149
 
1150
+ add_action( 'admin_enqueue_scripts', 'wprss_admin_notices_collection_enqueue_scripts' );
 
 
 
 
 
 
 
 
 
 
 
 
1151
  }
1152
 
1153
  return $collection;
1154
  }
1155
 
1156
+ /**
1157
+ * Enqueues the scripts for a notice collection.
1158
+ *
1159
+ * @since 4.7.8
1160
+ * @uses-filter wprss_admin_notice_collection_before_enqueue_scripts To modify list of script handles to enqueue.
1161
+ * @uses-action wprss_admin_notice_collection_after_enqueue_scripts To access list of enqueued script handles.
1162
+ */
1163
+ function wprss_admin_notices_collection_enqueue_scripts() {
1164
+ // Get singleton collection
1165
+ $collection = wprss_admin_notice_get_collection();
1166
+
1167
+ // Get script handles via filter
1168
+ $script_handles = apply_filters( 'wprss_admin_notice_collection_before_enqueue_scripts', array( 'wprss-admin-notifications' ), $collection );
1169
+ // Iterate and enqueue scripts
1170
+ foreach ( $script_handles as $_idx => $_handle ) wp_enqueue_script( $_handle );
1171
+ // Post-enqueueing action
1172
+ do_action( 'wprss_admin_notice_collection_after_enqueue_scripts', $script_handles, $collection );
1173
+
1174
+ // Frontend settings
1175
+ $settings = apply_filters( 'wprss_admin_notice_collection_before_localize_vars', array(
1176
+ 'notice_class' => $collection->get_notice_base_class(),
1177
+ 'nonce_class' => $collection->get_nonce_base_class(),
1178
+ 'btn_close_class' => $collection->get_btn_close_base_class(),
1179
+ 'action_code' => wprss_admin_notice_get_action_code()
1180
+ ), $collection );
1181
+ wp_localize_script( 'aventura', 'adminNoticeGlobalVars', $settings);
1182
+ do_action( 'wprss_admin_notice_collection_after_localize_vars', $settings, $collection );
1183
+ }
1184
 
1185
  /**
1186
  * Centralizes access to the name of the AJAX action handler for dismissing admin notices.
includes/admin-debugging.php CHANGED
@@ -6,8 +6,8 @@
6
  * @subpackage Includes
7
  * @since 3.0
8
  * @author Jean Galea <info@jeangalea.com>
9
- * @copyright Copyright(c) 2012-2013, Jean Galea
10
- * @link http://www.wpmayor.com
11
  * @license http://www.gnu.org/licenses/gpl.html
12
  */
13
 
@@ -45,7 +45,27 @@
45
  )
46
  );
47
 
48
- $operations['error-log'] = apply_filters(
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  'wprss_debug_error_log_operation',
50
  array(
51
  'nonce' => 'wprss-clear-error-log',
@@ -157,23 +177,48 @@
157
  <?php
158
  }
159
 
160
-
161
  /**
162
- * Renders the Clear Log button
163
- *
164
- * @since 3.9.6
165
  */
166
- function wprss_debug_clear_log_button() {
167
  ?>
168
  <h3><?php _e( 'Error Log', WPRSS_TEXT_DOMAIN ); ?></h3>
169
 
170
  <textarea readonly="readonly" id="wprss-error-log-textarea"><?php echo wprss_get_log(); ?></textarea>
 
 
171
 
172
- <form action="edit.php?post_type=wprss_feed&page=wprss-debugging" method="POST">
173
- <?php wp_nonce_field( 'wprss-clear-error-log' );
174
- submit_button( __( 'Clear log', WPRSS_TEXT_DOMAIN ), 'button-primary', 'error-log', true ); ?>
 
 
 
 
 
 
 
 
 
 
175
  </form>
 
 
176
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
177
  <?php
178
  }
179
 
6
  * @subpackage Includes
7
  * @since 3.0
8
  * @author Jean Galea <info@jeangalea.com>
9
+ * @copyright Copyright(c) 2012-2015, Jean Galea
10
+ * @link http://www.wprssaggregator.com
11
  * @license http://www.gnu.org/licenses/gpl.html
12
  */
13
 
45
  )
46
  );
47
 
48
+ $operations['render-error-log'] = apply_filters(
49
+ 'wprss_render_error_log_operation',
50
+ array(
51
+ 'nonce' => null,
52
+ 'run' => null,
53
+ 'redirect' => 'edit.php?post_type=wprss_feed&page=wprss-debugging',
54
+ 'render' => 'wprss_debug_render_error_log'
55
+ )
56
+ );
57
+
58
+ $operations['download-error-log'] = apply_filters(
59
+ 'wprss_debug_download_error_log_operation',
60
+ array(
61
+ 'nonce' => 'wprss-download-error-log',
62
+ 'run' => 'wprss_download_log',
63
+ 'redirect' => 'edit.php?post_type=wprss_feed&page=wprss-debugging',
64
+ 'render' => 'wprss_debug_download_log_button'
65
+ )
66
+ );
67
+
68
+ $operations['clear-error-log'] = apply_filters(
69
  'wprss_debug_error_log_operation',
70
  array(
71
  'nonce' => 'wprss-clear-error-log',
177
  <?php
178
  }
179
 
 
180
  /**
181
+ * Renders the Error Log.
 
 
182
  */
183
+ function wprss_debug_render_error_log() {
184
  ?>
185
  <h3><?php _e( 'Error Log', WPRSS_TEXT_DOMAIN ); ?></h3>
186
 
187
  <textarea readonly="readonly" id="wprss-error-log-textarea"><?php echo wprss_get_log(); ?></textarea>
188
+ <?php
189
+ }
190
 
191
+ /**
192
+ * Renders the "Clear log" button
193
+ *
194
+ * @since 3.9.6
195
+ */
196
+ function wprss_debug_clear_log_button() {
197
+ $form_url = admin_url( 'edit.php?post_type=wprss_feed&page=wprss-debugging' ); ?>
198
+ <form id="wprss-clear-error-log-form" action="<?php echo $form_url; ?>" method="POST" class="wprss-error-log-action">
199
+ <?php wp_nonce_field( 'wprss-clear-error-log' ); ?>
200
+ <button type="submit" for="wprss-clear-error-log-form" name="clear-error-log" class="button button-red">
201
+ <i class="fa fa-trash-o"></i>
202
+ <?php _e( 'Clear log', WPRSS_TEXT_DOMAIN ); ?>
203
+ </button>
204
  </form>
205
+ <?php
206
+ }
207
 
208
+ /**
209
+ * Renders the "Download Error Log" button
210
+ *
211
+ * @since 4.7.8
212
+ */
213
+ function wprss_debug_download_log_button() {
214
+ $form_url = admin_url( 'edit.php?post_type=wprss_feed&page=wprss-debugging' ); ?>
215
+ <form id="wprss-download-error-log-form" action="<?php echo $form_url; ?>" method="POST" class="wprss-error-log-action">
216
+ <?php wp_nonce_field( 'wprss-download-error-log' ); ?>
217
+ <button type="submit" for="wprss-download-error-log-form" name="download-error-log" class="button button-primary">
218
+ <i class="fa fa-download"></i>
219
+ <?php _e( 'Download log', WPRSS_TEXT_DOMAIN ); ?>
220
+ </button>
221
+ </form>
222
  <?php
223
  }
224
 
includes/admin-help.php CHANGED
@@ -1,9 +1,12 @@
1
  <?php
 
 
 
2
  /**
3
  * Build the Help page
4
- *
5
  * @since 4.2
6
- */
7
  function wprss_help_page_display() {
8
  ?>
9
 
@@ -12,14 +15,32 @@
12
 
13
  <h2><?php _e( 'Help & Support', WPRSS_TEXT_DOMAIN ); ?></h2>
14
  <h3><?php _e( 'Documentation', WPRSS_TEXT_DOMAIN ) ?></h3>
15
- <?php echo wpautop( __('In the <a href="http://www.wprssaggregator.com/documentation/">documentation area</a> on the WP RSS Aggregator website you will find comprehensive details on how to use the core plugin and all the add-ons.
16
-
17
- There are also some videos to help you make a quick start to setting up and enjoying this plugin.', WPRSS_TEXT_DOMAIN) ) ?>
 
 
 
 
 
 
 
 
 
18
  <h3><?php _e( 'Frequently Asked Questions (FAQ)', WPRSS_TEXT_DOMAIN ) ?></h3>
19
- <?php echo wpautop( __('If after going through the documentation you still have questions, please take a look at the <a href="http://www.wprssaggregator.com/faq/">FAQ page</a> on the site, we set this up purposely to answer the most commonly asked questions by our users.', WPRSS_TEXT_DOMAIN) ) ?>
20
-
21
  <?php
22
- if ( wprss_is_premium_user() ) {
 
 
 
 
 
 
 
 
 
 
 
23
  wprss_premium_help_display();
24
  } else {
25
  wprss_free_help_display();
@@ -32,9 +53,9 @@
32
 
33
  /**
34
  * Print the premium help section with inline support form.
35
- *
36
  * @since 4.7
37
- */
38
  function wprss_premium_help_display() {
39
  // Get the first valid license.
40
  $addon = '';
@@ -57,7 +78,7 @@
57
  }
58
 
59
  // Get the full license info so we can prefill the name and email
60
- $license = wprss_edd_check_license($addon, NULL, 'ALL');
61
  $customer_name = is_object($license) ? $license->customer_name : '';
62
  $customer_email = is_object($license) ? $license->customer_email : '';
63
 
@@ -89,11 +110,11 @@
89
  <td colspan="3"><input type='checkbox' name='support-include-log' value='checked' checked><?php _e('WP RSS Aggregator log file', WPRSS_TEXT_DOMAIN); ?></td>
90
  </tr>
91
  <tr>
92
- <td colspan="3"><input type='checkbox' name='support-include-sys' value='checked' checked><?php _e('WordPress information', WPRSS_TEXT_DOMAIN); ?></td>
93
  </tr>
94
  </table>
95
  </form>
96
- <div style='line-height:2.3em;'>
97
  <button id='send-message-btn' class='button button-primary'><?php _e('Send Message', WPRSS_TEXT_DOMAIN); ?></button>
98
  <span id='support-error'></span>
99
  </div>
@@ -103,12 +124,17 @@
103
 
104
  /**
105
  * Print the free help section with link to forums.
106
- *
107
  * @since 4.7
108
- */
109
  function wprss_free_help_display() {
110
  echo '<h3>' . __( 'Support Forums', WPRSS_TEXT_DOMAIN ) . '</h3>';
111
- echo wpautop( __( "Users of the free version of WP RSS Aggregator can ask questions on the " . '<a href="http://wordpress.org/support/plugin/wp-rss-aggregator">support forum</a>.', WPRSS_TEXT_DOMAIN ) );
 
 
 
 
 
112
  }
113
 
114
 
@@ -137,8 +163,8 @@
137
  // Send the email.
138
  $sent = wp_mail( "support@wprssaggregator.com", $subject, $message, $headers );
139
 
140
- // NB, the retval is a best-guess about email sending. According to the WP Codex it
141
- // doesn't mean the user received the email, it "only means that the method used
142
  // was able to process the request without any errors."
143
  if ($sent === FALSE) {
144
  $ret['error'] = sprintf(__('There was an error sending the form. Please use the <a href="%s" target="_blank">contact form on our site.</a>'), esc_attr('http://www.wprssaggregator.com/contact/'));
@@ -153,10 +179,10 @@
153
 
154
 
155
  /**
156
- * Ensures that all support form fields have been filled out. Returns TRUE
157
  *
158
  * @since 4.7
159
- * @return FALSE when all fields are valid, or a string containing an error they aren't.
160
  */
161
  function wprss_validate_support_request() {
162
  $fields = array(
@@ -171,7 +197,7 @@
171
  if (!isset($_GET[$field]) || $_GET[$field] === "") {
172
 
173
  return sprintf(
174
- __('Please fill out all the fields in the form, including the <strong>%s</strong> field.', WPRSS_TEXT_DOMAIN),
175
  ucfirst(substr($field, strpos($field, '-') + 1))
176
  );
177
  }
@@ -245,44 +271,44 @@
245
  return $headers;
246
  }
247
 
248
-
249
  /**
250
  * Encapsulates features for providing inline help in the admin interface.
251
- *
252
  * The following filters are introduced:
253
- *
254
  * - `wprss_help_default_options` - The default options to be extended.
255
- *
256
  * 1. The array of options
257
- *
258
  * - `wprss_help_template_path` - The path of template retrieved by WPRSS_Help::get_template().
259
- *
260
  * 1. The path to the template.
261
  * 2. The array of variables passed.
262
- *
263
  * - `wprss_help_template_vars` - The variables for the template, received by WPRSS_Help::get_template().
264
  *
265
  * 1. The variables array.
266
  * 2. The path to the template, filtered by `wprss_help_template_path`.
267
- *
268
  * - `wprss_help_tooltip_options` - Options that are in effect when adding tooltips with WPRSS_Help::add_tooltip().
269
  * - `wprss_help_tooltip_handle_html_options` - Options that are in effect when retrieving tooltip handle HTML with WPRSS_Help::wprss_help_tooltip_handle_html_options.
270
- *
271
- *
272
  * Also, the following options are available:
273
- *
274
  * - `tooltip_id_prefix` - The HTML element ID prefix that will be used for tooltips.
275
  * - `tooltip_handle_text` - The text that will appear inside the handle HTML elements.
276
  * - `tooltip_handle_class` - The CSS class that will be assigned to tooltip handles.
277
  * - `tooltip_content_class` - The CSS class that will be assigned to tooltip content HTML elements.
278
  * - `enqueue_tooltip_content` - Whether or not content is to be enqueued, instead of being output directly.
279
- *
280
  * 1. The absolute path to the core plugin directory
281
  */
282
  class WPRSS_Help {
283
 
284
  static $_instance;
285
-
286
  protected $_options;
287
  protected $_enqueued_tooltip_content = array();
288
  protected $_tooltips = array();
@@ -293,14 +319,14 @@ class WPRSS_Help {
293
  const TEXT_DOMAIN = WPRSS_TEXT_DOMAIN;
294
  const HASHING_CONCATENATOR = '|';
295
  const OPTIONS_FILTER_SUFFIX = '_options';
296
-
297
  const TOOLTIP_DATA_KEY_ID = 'id';
298
  const TOOLTIP_DATA_KEY_TEXT = 'text';
299
  const TOOLTIP_DATA_KEY_OPTIONS = 'options';
300
 
301
  /**
302
  * Retrieve the singleton instance
303
- *
304
  * @return WPRSS_Help
305
  */
306
  public static function get_instance() {
@@ -311,20 +337,20 @@ class WPRSS_Help {
311
 
312
  return self::$_instance;
313
  }
314
-
315
 
316
  public static function init() {
317
  // Actions
318
  add_action( 'admin_enqueue_scripts', array( self::get_instance(), '_admin_enqueue_scripts' ) );
319
  add_action( 'admin_footer', array( self::get_instance(), '_admin_footer' ) );
320
  }
321
-
322
 
323
  /**
324
  * Filters used:
325
- *
326
  * - `wprss_help_default_options`
327
- *
328
  * @param array $options Options that will overwrite defaults.
329
  */
330
  public function __construct( $options = array() ) {
@@ -347,21 +373,21 @@ class WPRSS_Help {
347
 
348
  $this->_construct();
349
  }
350
-
351
 
352
  /**
353
  * Used for parameter-less extension of constructor logic
354
  */
355
  protected function _construct() {
356
-
357
  }
358
-
359
 
360
  /**
361
  * Return an option value, or the whole array of internal options.
362
  * These options are a product of the defaults, the database, and anything
363
  * set later on, applied on top of eachother and overwriting in that order.
364
- *
365
  * @param null|string $key The key of the option to return.
366
  * @param null|mixed $default What to return if options with the specified key not found.
367
  * @return array|mixed|null The option value, or an array of options.
@@ -372,20 +398,20 @@ class WPRSS_Help {
372
  if ( is_null( $key ) ) {
373
  return $options;
374
  }
375
-
376
  if( is_array( $key ) ) {
377
  return $this->array_merge_recursive_distinct( $options, $key );
378
  }
379
 
380
  return isset( $options[ $key ] ) ? $options[ $key ] : $default;
381
  }
382
-
383
 
384
  /**
385
  * Set the value of an internal option or options.
386
  * Existing options will be overwritten. New options will be added.
387
  * Database options will not be modified.
388
- *
389
  * @param string|array $key The key of the option to set, or an array of options.
390
  * @param null|mixed $value The value of the option to set.
391
  * @return WPRSS_Help This instance.
@@ -401,12 +427,12 @@ class WPRSS_Help {
401
 
402
  $this->_set_options( $key, $value );
403
  }
404
-
405
 
406
  /**
407
  * Set an option value, or all options.
408
  * In latter case completely overrides the whole options array.
409
- *
410
  * @param string|array $key The key of the option to set, or the whole options array.
411
  * @param null|mixed $value Value of the option to set.
412
  * @return WPRSS_Help This instance.
@@ -420,11 +446,11 @@ class WPRSS_Help {
420
  $this->_options[ $key ] = $value;
421
  return $this;
422
  }
423
-
424
 
425
  /**
426
  * Returns a WPRSS_Help option or options from the database.
427
- *
428
  * @param string $key The key of the option to return.
429
  * @param null|mixed $default What to return if option identified by $key is not found.
430
  * @return null|array|mixed The options or option value.
@@ -438,16 +464,16 @@ class WPRSS_Help {
438
 
439
  return isset( $options[ $key ] ) ? $options[ $key ] : $default;
440
  }
441
-
442
 
443
  /**
444
  * Get content of a template.
445
- *
446
  * Filters used
447
- *
448
  * - `wprss_help_template_path`
449
  * - `wprss_help_template_vars`
450
- *
451
  * @param string $path Full path to the template
452
  * @param array $vars This will be passed to the template
453
  */
@@ -465,68 +491,68 @@ class WPRSS_Help {
465
 
466
  return $content;
467
  }
468
-
469
 
470
  /**
471
  * This is called during the `admin_enqueue_scripts` action, and will
472
  * enqueue scripts needed for the backend.
473
- *
474
  * Filters used:
475
- *
476
  * - `wprss_help_admin_scripts`
477
- *
478
  * @return WPRSS_Help This instance.
479
  */
480
  public function _admin_enqueue_scripts() {
481
  $scripts = $this->apply_filters( 'admin_scripts', array(
482
  'jquery-ui-tooltip' => array()
483
  ));
484
-
485
  foreach ( $scripts as $_handle => $_args ) {
486
  // Allows numeric array with handles as values
487
  if ( is_numeric( $_handle ) ) {
488
  $_handle = $_args;
489
  }
490
-
491
  // Allows specifying null as value to simply enqueue handle
492
  if ( empty( $_args ) ){
493
  $_args = array();
494
  }
495
-
496
  array_unshift( $_args, $_handle );
497
  call_user_func_array( 'wp_enqueue_script', $_args );
498
  }
499
-
500
  return $this;
501
  }
502
-
503
-
504
  public function _admin_footer() {
505
  $html = '';
506
  $html .= $this->get_enqueued_tooltip_content_html() . "\n";
507
  $html .= $this->get_admin_footer_js_html();
508
  $html = $this->apply_filters( 'admin_footer', $html );
509
-
510
  echo $html;
511
  }
512
-
513
-
514
  public function is_overrides_default_prefix( $string ) {
515
  return strpos( $string, self::OVERRIDE_DEFAULT_PREFIX ) === 0;
516
  }
517
-
518
-
519
  /**
520
  * @return string This class's text domain
521
  */
522
  public function get_text_domain() {
523
  return self::TEXT_DOMAIN;
524
  }
525
-
526
  /**
527
  * Format this string, replacing placeholders with values, and translate it
528
  * in the class's text domain.
529
- *
530
  * @see sprintf()
531
  * @param string $string The string to translate.
532
  * @param mixed $argN,.. Additional arguments.
@@ -534,18 +560,18 @@ class WPRSS_Help {
534
  public function __( $string, $argN = null ) {
535
  $args = func_get_args();
536
  $args[0] = $string = __( $string, $this->get_text_domain() );
537
-
538
  $string = call_user_func_array( 'sprintf', $args );
539
-
540
  return $string;
541
  }
542
-
543
  /**
544
  * Hashes all the given values into a single hash.
545
  * Accepts an infinite number of parameters, all of which will be first
546
  * glued together by a separator, then hashed.
547
  * Non-scalar values will be serialized.
548
- *
549
  * @param mixed $value The value to hash.
550
  * @param mixed $argN Other values to hash.
551
  * @return string The hash.
@@ -553,33 +579,33 @@ class WPRSS_Help {
553
  public function get_hash( $value ) {
554
  $args = func_get_args();
555
  $glue = self::HASHING_CONCATENATOR;
556
-
557
  $blob = '';
558
  foreach ( $args as $_idx => $_arg ) {
559
  $blob .= is_scalar( $_arg ) ? $_arg : serialize( $_arg );
560
  $blob .= $glue;
561
  }
562
-
563
  $blob = substr( $blob, 0, -1 );
564
-
565
  return sha1( $blob );
566
  }
567
-
568
  /**
569
  * Get the class code prefix, or the specified prefixed with it.
570
- *
571
  * @param string $string A string to prefix.
572
  * @return string The code prefix or the prefixed string.
573
  */
574
  public function get_code_prefix( $string = '' ) {
575
  return self::CODE_PREFIX . (string)$string;
576
  }
577
-
578
  /**
579
  * Optionally prefix a string with the class code prefix, unless it
580
  * contains the "!" character in the very beginning, in which case it will
581
  * simply be removed.
582
- *
583
  * @param string $string The string to consider for prefixing.
584
  * @return string The prefixed or clean string.
585
  */
@@ -588,11 +614,11 @@ class WPRSS_Help {
588
  ? substr( $string, 1 )
589
  : $this->get_code_prefix( $string );
590
  }
591
-
592
  /**
593
  * Applies filters, but prefixes the filter name with 'wprss_help_',
594
  * unless '!' is specified as the first character of the filter.
595
- *
596
  * @param string $filter_name Name or "tag" of the filter.
597
  * @param mixed $subject The value to apply filters to.
598
  * @param mixed $argN,.. Additional filter arguments
@@ -600,46 +626,46 @@ class WPRSS_Help {
600
  */
601
  public function apply_filters( $filter_name, $subject, $argN = null ) {
602
  $args = func_get_args();
603
-
604
  $args[0] = $filter_name = $this->prefix( $filter_name );
605
-
606
  return call_user_func_array( 'apply_filters', $args );
607
  }
608
-
609
-
610
  /**
611
  * Applies a filters with the specified name to the options that were
612
  * applied on top of defaults.
613
  * The name will be prefixed with the class prefix 'wprss_help_', and
614
  * suffixed with '_options'.
615
- *
616
  * @param string $filter_name Name of the filter to apply to the options
617
  * @param array $options The options to filter
618
  * @param mixed $filter_argN,.. Other filter arguments to be passed to filter
619
  */
620
  public function apply_options_filters( $filter_name, $options = array(), $filter_argN = null ) {
621
  $args = func_get_args();
622
-
623
  // Adding sufix
624
  $args[0] = $filter_name .= self::OPTIONS_FILTER_SUFFIX;
625
-
626
  // Applying defaults
627
  $args[1] = $options = $this->get_options( $options );
628
-
629
  // Entry point. Order of args is already correct.
630
  $options = call_user_func_array( array( $this, 'apply_filters' ), $args );
631
-
632
  return $options;
633
  }
634
-
635
-
636
  /**
637
  * Parses the tooltip handle template path for placeholders.
638
- *
639
  * Filters used:
640
- *
641
  * - `wprss_help_admin_footer_js_html_template`
642
- *
643
  * @param null|string $path Optional path to parse and retrieve. Default: value of the 'admin_footer_js_template' option.
644
  * @return string Path to the template.
645
  */
@@ -648,40 +674,40 @@ class WPRSS_Help {
648
  if ( is_null( $path ) ) {
649
  $path = $this->get_options( 'admin_footer_js_template' );
650
  }
651
-
652
  // Entry point
653
  $path = $this->apply_filters( 'admin_footer_js_html_template', $path );
654
-
655
  return $this->parse_path( $path );
656
  }
657
-
658
-
659
  /**
660
  * Get the HTML of the JavaScript for the footer in Admin Panel.
661
- *
662
  * Filters used:
663
- *
664
  * - `wprss_help_admin_footer_js_html`
665
- *
666
  * @param array $options Any additional options to be used with defaults.
667
  * @return string The HTML.
668
  */
669
  public function get_admin_footer_js_html( $options = array() ) {
670
  $options = $this->apply_options_filters( 'admin_footer_js_html', $options);
671
-
672
  $templatePath = $this->get_admin_footer_js_html_template( $options['admin_footer_js_template'] );
673
-
674
  return $this->get_template($templatePath, $options);
675
  }
676
-
677
-
678
  /**
679
  * Parses the tooltip handle template path for placeholders.
680
- *
681
  * Filters used:
682
- *
683
  * - `wprss_help_tooltip_handle_html_template`
684
- *
685
  * @param null|string $path Optional path to parse and retrieve. Default: value of the 'tooltip_handle_template' option.
686
  * @return string Path to the template.
687
  */
@@ -690,21 +716,21 @@ class WPRSS_Help {
690
  if ( is_null( $path ) ) {
691
  $path = $this->get_options( 'tooltip_handle_template' );
692
  }
693
-
694
  // Entry point
695
  $path = $this->apply_filters( 'tooltip_handle_html_template', $path );
696
-
697
  return $this->parse_path( $path );
698
  }
699
-
700
-
701
  /**
702
  * Get the HTML of the tooltip handle.
703
- *
704
  * Filters used:
705
- *
706
  * - `wprss_help_tooltip_handle_html_options`
707
- *
708
  * @param string $text Content of the tooltip text.
709
  * @param string $id ID of the tooltip.
710
  * @param array $options Any additional options to be used with defaults.
@@ -716,20 +742,20 @@ class WPRSS_Help {
716
  // Add template varialbes
717
  $options['tooltip_id'] = $id;
718
  $options['tooltip_text'] = $text;
719
-
720
  $templatePath = $this->get_tooltip_handle_html_template( $options['tooltip_handle_template'] );
721
-
722
  return $this->get_template($templatePath, $options);
723
  }
724
-
725
-
726
  /**
727
  * Parses the tooltip content template path for placeholders.
728
- *
729
  * Filters used:
730
- *
731
  * - `wprss_help_tooltip_content_html_template`
732
- *
733
  * @param null|string $path Optional path to parse and retrieve. Default: value of the 'tooltip_handle_template' option.
734
  * @return string Path to the template.
735
  */
@@ -738,21 +764,21 @@ class WPRSS_Help {
738
  if ( is_null( $path ) ) {
739
  $path = $this->get_options( 'tooltip_content_template' );
740
  }
741
-
742
  // Entry point
743
  $path = $this->apply_filters( 'tooltip_content_html_template', $path );
744
-
745
  return $this->parse_path( $path );
746
  }
747
-
748
-
749
  /**
750
  * Get the HTML of the tooltip content.
751
- *
752
  * Filters used:
753
- *
754
  * - `wprss_help_tooltip_content_html_options`
755
- *
756
  * @param string $text Content of the tooltip text.
757
  * @param string $id ID of the tooltip.
758
  * @param array $options Any additional options to be used with defaults.
@@ -760,23 +786,23 @@ class WPRSS_Help {
760
  */
761
  public function get_tooltip_content_html( $text, $id, $options = array() ) {
762
  $options = $this->apply_options_filters( 'tooltip_content_html', $options, $text, $id );
763
-
764
  // Add template varialbes
765
  $options['tooltip_id'] = $id;
766
  $options['tooltip_text'] = $text;
767
-
768
  $templatePath = $this->get_tooltip_content_html_template( $options['tooltip_content_template'] );
769
-
770
  return $this->get_template( $templatePath, $options );
771
  }
772
-
773
-
774
  /**
775
  * Add tooltip and get tooltip HTML.
776
  * If $text is null, just get the HTML of tooltip with specified ID.
777
  * The `is_enqueue_tooltip_content` option determines whether to enqueue
778
  * the content, instead of outputting it after the handle.
779
- *
780
  * @param string $id ID for this tooltip
781
  * @param string|null $text Text of this tooltip. If null, tooltip will not be added, but only retrieved.
782
  * @param array|bool $options The options for this operation, or a boolean indicating whether or not content is to be enqueued
@@ -786,11 +812,11 @@ class WPRSS_Help {
786
  $this->add_tooltip( $id, $text, $options );
787
  return $this->do_tooltip( $id );
788
  }
789
-
790
-
791
  /**
792
  * Add tooltips in a batch, with optionally prefixed ID.
793
- *
794
  * @param array $tooltips An array where key is tooltip ID and value is tooltip text.
795
  * @param string $prefix A prefix to add to all tooltip IDs.
796
  * @param array $options Arra of options for all the tooltips to add.
@@ -799,20 +825,20 @@ class WPRSS_Help {
799
  public function add_tooltips( $tooltips, $prefix = null, $options = array() ) {
800
  $prefix = (string) $prefix;
801
  if ( !is_array($options) ) $options = array();
802
-
803
  foreach ( $tooltips as $_id => $_text ) {
804
  $this->add_tooltip( $prefix . $_id, $_text, $options );
805
  }
806
-
807
  return $this;
808
  }
809
-
810
-
811
  /**
812
  * Add a tooltip for later display.
813
  * Text and options will be replaced by existing text and options, if they
814
  * are empty, and a tooltip with the same ID is already registered.
815
- *
816
  * @param string $id The ID of this tooltip
817
  * @param string $text Text for this tooltip
818
  * @param array $options Options for this tooltip.
@@ -823,16 +849,16 @@ class WPRSS_Help {
823
  if ( is_null( $text ) ) $text = isset( $tooltip[ self::TOOLTIP_DATA_KEY_TEXT ] ) ? $tooltip[ self::TOOLTIP_DATA_KEY_TEXT ] : $text;
824
  if ( empty( $options ) ) $options = isset( $tooltip[ self::TOOLTIP_DATA_KEY_OPTIONS ] ) ? $tooltip[ self::TOOLTIP_DATA_KEY_OPTIONS ] : $options;
825
  }
826
-
827
  $this->set_tooltip( $id, $text, $options );
828
-
829
  return $this;
830
  }
831
-
832
-
833
  /**
834
  * Set a tooltip, existing or not.
835
- *
836
  * @param string $id The ID of this tooltip
837
  * @param string $text Text for this tooltip
838
  * @param array $options Options for this tooltip.
@@ -844,14 +870,14 @@ class WPRSS_Help {
844
  self::TOOLTIP_DATA_KEY_TEXT => $text,
845
  self::TOOLTIP_DATA_KEY_OPTIONS => $options
846
  );
847
-
848
  return $this;
849
  }
850
-
851
-
852
  /**
853
  * Retrieve one tooltip, or an array containing all tooltips.
854
- *
855
  * @param string|null $id The ID of the tooltip to retrieve.
856
  * @param mixed|null $default What to return if tooltip with specified ID not found.
857
  * @return array An array that contains the following indexes: 'id', 'text', 'options'. See {@link add_tooltip()} for details.
@@ -860,28 +886,28 @@ class WPRSS_Help {
860
  if ( is_null( $id ) ) {
861
  return $this->_tooltips;
862
  }
863
-
864
  return $this->has_tooltip( $id ) ? $this->_tooltips[ $id ] : $default;
865
  }
866
-
867
-
868
  /**
869
  * Check whether a tooltip with the specified ID exists.
870
- *
871
  * @param string $id ID of the tooltip to check for.
872
  * @return boolean True if a tooltip with the specified ID exists; false otherwise.
873
  */
874
  public function has_tooltip( $id ) {
875
  return isset( $this->_tooltips[ $id ] );
876
  }
877
-
878
  /**
879
  * Get registered tooltip HTML.
880
- *
881
  * Filters used:
882
- *
883
  * - `wprss_help_tooltip_options` - Filters options used for tooltip
884
- *
885
  * @param string $id ID for this tooltip
886
  * @param string $text Text of this tooltip
887
  * @param array|bool $options The options for this operation, or a boolean indicating whether or not content is to be enqueued
@@ -889,40 +915,40 @@ class WPRSS_Help {
889
  */
890
  public function do_tooltip( $id ) {
891
  $options = $this->get_options();
892
-
893
  if ( !($tooltip = $this->get_tooltip( $id )) || !isset($tooltip[ self::TOOLTIP_DATA_KEY_TEXT ]) || !$tooltip[ self::TOOLTIP_DATA_KEY_TEXT ] ) {
894
  return isset( $options['tooltip_not_found_handle_html'] )
895
  ? $options['tooltip_not_found_handle_html']
896
  : null;
897
  }
898
-
899
  $options = isset( $tooltip[ self::TOOLTIP_DATA_KEY_OPTIONS ] ) ? $tooltip[ self::TOOLTIP_DATA_KEY_OPTIONS ] : null;
900
  $text = isset( $tooltip[ self::TOOLTIP_DATA_KEY_TEXT ] ) ? $tooltip[ self::TOOLTIP_DATA_KEY_TEXT ] : null;
901
-
902
  if ( !is_array( $options ) ) {
903
  $options = array( 'is_enqueue_tooltip_content' => $options );
904
  }
905
-
906
  // Entry point
907
  $options = $this->apply_options_filters( 'tooltip', $options, $id, $text );
908
-
909
  // Get handle HTML
910
  $output = $this->get_tooltip_handle_html( $text, $id, $options );
911
-
912
  if ( $this->evaluate_boolean( $options['is_enqueue_tooltip_content'] ) ) {
913
  $this->enqueue_tooltip_content($text, $id, $options);
914
  }
915
  else {
916
  $output .= $this->get_tooltip_content_html( $text, $id, $options );
917
  }
918
-
919
  return $output;
920
  }
921
-
922
-
923
  /**
924
  * Enqueue tooltip content to be displayed in another part of the page.
925
- *
926
  * @param string $text The text of the tooltip content to enqueue.
927
  * @param string $id ID of the tooltip, the content of which to enqueue.
928
  * @param array $options This tooltip's options.
@@ -930,7 +956,7 @@ class WPRSS_Help {
930
  */
931
  public function enqueue_tooltip_content( $text, $id, $options = array() ) {
932
  $queue_method = $this->apply_filters( 'enqueue_tooltip_content_method', array( $this, '_enqueue_tooltip_content' ), $options, $id, $text );
933
-
934
  // "Error handling" WP style
935
  if ( !is_callable( $queue_method ) ) {
936
  return new WP_Error( $this->prefix( 'invalid_queue_method' ), $this->__( 'Could not enqueue tooltip content: the queue method is not a valid callable.' ), array(
@@ -940,13 +966,13 @@ class WPRSS_Help {
940
  'options' => $options
941
  ));
942
  }
943
-
944
  call_user_func_array( $queue_method, array( $text, $id, $options ) );
945
-
946
  return $this;
947
  }
948
-
949
-
950
  public function _enqueue_tooltip_content( $text, $id, $options = array() ) {
951
  $hash = $this->get_hash( $text, $id, $options );
952
  $this->_enqueued_tooltip_content[ $hash ] = array(
@@ -954,31 +980,31 @@ class WPRSS_Help {
954
  self::TOOLTIP_DATA_KEY_ID => $id,
955
  self::TOOLTIP_DATA_KEY_OPTIONS => $options
956
  );
957
-
958
  return $this;
959
  }
960
-
961
-
962
  public function get_enqueued_tooltip_content() {
963
  return $this->_enqueued_tooltip_content;
964
  }
965
-
966
-
967
  public function get_enqueued_tooltip_content_html() {
968
  $output = '';
969
  foreach ( $this->get_enqueued_tooltip_content() as $_hash => $_vars ) {
970
  $options = is_array( $_vars[ self::TOOLTIP_DATA_KEY_OPTIONS ] ) ? $_vars[ self::TOOLTIP_DATA_KEY_OPTIONS ] : array();
971
  $output = $this->get_tooltip_content_html( $_vars[ self::TOOLTIP_DATA_KEY_ID ], $_vars[ self::TOOLTIP_DATA_KEY_ID ], $options );
972
  }
973
-
974
  echo $output;
975
  }
976
-
977
-
978
  /**
979
  * Check whether or not the given value is false.
980
  * False values are all {@link empty()} values, and also strings 'false' and 'no'.
981
- *
982
  * @param mixed $value The value to check.
983
  * @return boolean Whether or not the value is considered to be false.
984
  */
@@ -987,12 +1013,12 @@ class WPRSS_Help {
987
  ? false
988
  : true;
989
  }
990
-
991
 
992
  /**
993
  * Merge two arrays in an intuitive way.
994
  * Input arrays remain unchanged.
995
- *
996
  * @see http://php.net/manual/en/function.array-merge-recursive.php#92195
997
  * @param array $array1 The array to merge.
998
  * @param array $array2 The array to merge into.
@@ -1011,54 +1037,54 @@ class WPRSS_Help {
1011
 
1012
  return $merged;
1013
  }
1014
-
1015
-
1016
  /**
1017
  * Converts an array to a numeric array.
1018
  * If $map is empty, assumes that the array keys are already in order.
1019
  * If $map is a number, assumes it's the amount of elements to return.
1020
  * If $map is an array, assumes it is the map of intended numeric indexes to their value in the input array.
1021
- *
1022
  * @param array $array The array to convert to a numeric array
1023
  * @param false|null|array $map The map of the array indexes, or number of array elements to slice, or nothing.
1024
  * @return array The resulting numeric array.
1025
  */
1026
  public function array_to_numeric( $array, $map = null ) {
1027
  $result = array();
1028
-
1029
  // If map is not an array, assume it's an indicator
1030
  if ( !is_array( $map ) ) {
1031
  $array = array_values( $array );
1032
  }
1033
-
1034
  // If map is empty, assume keys are in order
1035
  if ( empty( $map ) ) {
1036
  return $array;
1037
  }
1038
-
1039
  // If map is a number, assume it's the amount of elements to return
1040
  if ( is_numeric( $map ) ) {
1041
  $map = intval( $map );
1042
  return array_slice( $array, 0, $map );
1043
  }
1044
-
1045
  foreach( $map as $_idx => $_key ) {
1046
  $result[ $_idx ] = $array[ $_key ];
1047
  }
1048
-
1049
  return $result;
1050
  }
1051
-
1052
-
1053
  /**
1054
  * Parses the template and replaces placeholders with their values.
1055
  * This function uses {@see sprintf()} to format the template string using
1056
  * the values provided in $data.
1057
  * It is also possible for $data to be an associative array of key-value pairs.
1058
- * To achieve the same result, a map can be provided, mapping data keys to
1059
  * their placeholder positions.
1060
- * If no map is provided,
1061
- *
1062
  * @param string $string The template string.
1063
  * @param array $data The key-value pairs of template data.
1064
  * @param false|null|array $map {@see array_to_numeric()} The template value map.
@@ -1069,18 +1095,18 @@ class WPRSS_Help {
1069
  array_unshift( $data, $string );
1070
  return call_user_func_array( 'sprintf', $data );
1071
  }
1072
-
1073
-
1074
  /**
1075
  * Parses a path template specifically with WPRSS_Help path placeholders.
1076
- *
1077
  * Filters used (in order):
1078
- *
1079
  * 1. `parse_path_data_default`;
1080
  * 2. `parse_path_data`;
1081
  * 3. `parse_path_map`;
1082
  * 4. `parse_path_path`.
1083
- *
1084
  * @see WPRSS_Help::parse_template()
1085
  * @param string $path The path to parse.
1086
  * @param null|array $data Any additional data. Will be merged with defaults.
@@ -1091,7 +1117,7 @@ class WPRSS_Help {
1091
  if( is_null( $data ) ) {
1092
  $data = array();
1093
  }
1094
-
1095
  $defaults = $this->apply_filters( 'parse_path_data_default', array(
1096
  'wprss_templates_dir' => wprss_get_templates_dir()
1097
  ));
@@ -1099,7 +1125,7 @@ class WPRSS_Help {
1099
  $data = $this->apply_filters( 'parse_path_data', $data, $path, $map );
1100
  $map = $this->apply_filters( 'parse_path_map', $map, $data, $path );
1101
  $path = $this->apply_filters( 'parse_path_path', $path, $data, $map );
1102
-
1103
  return $this->parse_template( $path, $data, $map );
1104
  }
1105
  }
1
  <?php
2
+
3
+ use Aventura\Wprss\Core\Licensing\License\Status as License_Status;
4
+
5
  /**
6
  * Build the Help page
7
+ *
8
  * @since 4.2
9
+ */
10
  function wprss_help_page_display() {
11
  ?>
12
 
15
 
16
  <h2><?php _e( 'Help & Support', WPRSS_TEXT_DOMAIN ); ?></h2>
17
  <h3><?php _e( 'Documentation', WPRSS_TEXT_DOMAIN ) ?></h3>
18
+ <?php
19
+ printf(
20
+ wpautop(
21
+ __( 'In the <a href="%s">documentation area</a> on the WP RSS Aggregator website you will find comprehensive details on how to use the core plugin and all the add-ons.
22
+
23
+ There are also some videos to help you make a quick start to setting up and enjoying this plugin.',
24
+ WPRSS_TEXT_DOMAIN
25
+ )
26
+ ),
27
+ 'http://docs.wprssaggregator.com/'
28
+ );
29
+ ?>
30
  <h3><?php _e( 'Frequently Asked Questions (FAQ)', WPRSS_TEXT_DOMAIN ) ?></h3>
 
 
31
  <?php
32
+ printf(
33
+ wpautop(
34
+ __( 'If after going through the documentation you still have questions, please take a look at the <a href="%s">FAQ page</a> on the site. We set this up purposely to answer the most commonly asked questions by our users.',
35
+ WPRSS_TEXT_DOMAIN
36
+ )
37
+ ),
38
+ 'http://docs.wprssaggregator.com/category/faqs/'
39
+ )
40
+ ?>
41
+
42
+ <?php
43
+ if ( wprss_licensing_get_manager()->licenseWithStatusExists( License_Status::VALID ) ) {
44
  wprss_premium_help_display();
45
  } else {
46
  wprss_free_help_display();
53
 
54
  /**
55
  * Print the premium help section with inline support form.
56
+ *
57
  * @since 4.7
58
+ */
59
  function wprss_premium_help_display() {
60
  // Get the first valid license.
61
  $addon = '';
78
  }
79
 
80
  // Get the full license info so we can prefill the name and email
81
+ $license = wprss_licensing_get_manager()->checkLicense( $addon, 'ALL' );
82
  $customer_name = is_object($license) ? $license->customer_name : '';
83
  $customer_email = is_object($license) ? $license->customer_email : '';
84
 
110
  <td colspan="3"><input type='checkbox' name='support-include-log' value='checked' checked><?php _e('WP RSS Aggregator log file', WPRSS_TEXT_DOMAIN); ?></td>
111
  </tr>
112
  <tr>
113
+ <td colspan="3"><input type='checkbox' name='support-include-sys' value='checked' checked><?php _e('WordPress debugging information', WPRSS_TEXT_DOMAIN); ?></td>
114
  </tr>
115
  </table>
116
  </form>
117
+ <div style='line-height:2.3em; margin-top:10px;'>
118
  <button id='send-message-btn' class='button button-primary'><?php _e('Send Message', WPRSS_TEXT_DOMAIN); ?></button>
119
  <span id='support-error'></span>
120
  </div>
124
 
125
  /**
126
  * Print the free help section with link to forums.
127
+ *
128
  * @since 4.7
129
+ */
130
  function wprss_free_help_display() {
131
  echo '<h3>' . __( 'Support Forums', WPRSS_TEXT_DOMAIN ) . '</h3>';
132
+ printf(
133
+ wpautop(
134
+ __( 'Users of the free version of WP RSS Aggregator can ask questions on the <a href="%s">support forum</a>.', WPRSS_TEXT_DOMAIN )
135
+ ),
136
+ 'http://wordpress.org/support/plugin/wp-rss-aggregator'
137
+ );
138
  }
139
 
140
 
163
  // Send the email.
164
  $sent = wp_mail( "support@wprssaggregator.com", $subject, $message, $headers );
165
 
166
+ // NB, the retval is a best-guess about email sending. According to the WP Codex it
167
+ // doesn't mean the user received the email, it "only means that the method used
168
  // was able to process the request without any errors."
169
  if ($sent === FALSE) {
170
  $ret['error'] = sprintf(__('There was an error sending the form. Please use the <a href="%s" target="_blank">contact form on our site.</a>'), esc_attr('http://www.wprssaggregator.com/contact/'));
179
 
180
 
181
  /**
182
+ * Ensures that all support form fields have been filled out. Returns TRUE
183
  *
184
  * @since 4.7
185
+ * @return FALSE when all fields are valid, or a string containing an error they aren't.
186
  */
187
  function wprss_validate_support_request() {
188
  $fields = array(
197
  if (!isset($_GET[$field]) || $_GET[$field] === "") {
198
 
199
  return sprintf(
200
+ __('Please fill out all the fields in the form, including the <strong>%s</strong> field.', WPRSS_TEXT_DOMAIN),
201
  ucfirst(substr($field, strpos($field, '-') + 1))
202
  );
203
  }
271
  return $headers;
272
  }
273
 
274
+
275
  /**
276
  * Encapsulates features for providing inline help in the admin interface.
277
+ *
278
  * The following filters are introduced:
279
+ *
280
  * - `wprss_help_default_options` - The default options to be extended.
281
+ *
282
  * 1. The array of options
283
+ *
284
  * - `wprss_help_template_path` - The path of template retrieved by WPRSS_Help::get_template().
285
+ *
286
  * 1. The path to the template.
287
  * 2. The array of variables passed.
288
+ *
289
  * - `wprss_help_template_vars` - The variables for the template, received by WPRSS_Help::get_template().
290
  *
291
  * 1. The variables array.
292
  * 2. The path to the template, filtered by `wprss_help_template_path`.
293
+ *
294
  * - `wprss_help_tooltip_options` - Options that are in effect when adding tooltips with WPRSS_Help::add_tooltip().
295
  * - `wprss_help_tooltip_handle_html_options` - Options that are in effect when retrieving tooltip handle HTML with WPRSS_Help::wprss_help_tooltip_handle_html_options.
296
+ *
297
+ *
298
  * Also, the following options are available:
299
+ *
300
  * - `tooltip_id_prefix` - The HTML element ID prefix that will be used for tooltips.
301
  * - `tooltip_handle_text` - The text that will appear inside the handle HTML elements.
302
  * - `tooltip_handle_class` - The CSS class that will be assigned to tooltip handles.
303
  * - `tooltip_content_class` - The CSS class that will be assigned to tooltip content HTML elements.
304
  * - `enqueue_tooltip_content` - Whether or not content is to be enqueued, instead of being output directly.
305
+ *
306
  * 1. The absolute path to the core plugin directory
307
  */
308
  class WPRSS_Help {
309
 
310
  static $_instance;
311
+
312
  protected $_options;
313
  protected $_enqueued_tooltip_content = array();
314
  protected $_tooltips = array();
319
  const TEXT_DOMAIN = WPRSS_TEXT_DOMAIN;
320
  const HASHING_CONCATENATOR = '|';
321
  const OPTIONS_FILTER_SUFFIX = '_options';
322
+
323
  const TOOLTIP_DATA_KEY_ID = 'id';
324
  const TOOLTIP_DATA_KEY_TEXT = 'text';
325
  const TOOLTIP_DATA_KEY_OPTIONS = 'options';
326
 
327
  /**
328
  * Retrieve the singleton instance
329
+ *
330
  * @return WPRSS_Help
331
  */
332
  public static function get_instance() {
337
 
338
  return self::$_instance;
339
  }
340
+
341
 
342
  public static function init() {
343
  // Actions
344
  add_action( 'admin_enqueue_scripts', array( self::get_instance(), '_admin_enqueue_scripts' ) );
345
  add_action( 'admin_footer', array( self::get_instance(), '_admin_footer' ) );
346
  }
347
+
348
 
349
  /**
350
  * Filters used:
351
+ *
352
  * - `wprss_help_default_options`
353
+ *
354
  * @param array $options Options that will overwrite defaults.
355
  */
356
  public function __construct( $options = array() ) {
373
 
374
  $this->_construct();
375
  }
376
+
377
 
378
  /**
379
  * Used for parameter-less extension of constructor logic
380
  */
381
  protected function _construct() {
382
+
383
  }
384
+
385
 
386
  /**
387
  * Return an option value, or the whole array of internal options.
388
  * These options are a product of the defaults, the database, and anything
389
  * set later on, applied on top of eachother and overwriting in that order.
390
+ *
391
  * @param null|string $key The key of the option to return.
392
  * @param null|mixed $default What to return if options with the specified key not found.
393
  * @return array|mixed|null The option value, or an array of options.
398
  if ( is_null( $key ) ) {
399
  return $options;
400
  }
401
+
402
  if( is_array( $key ) ) {
403
  return $this->array_merge_recursive_distinct( $options, $key );
404
  }
405
 
406
  return isset( $options[ $key ] ) ? $options[ $key ] : $default;
407
  }
408
+
409
 
410
  /**
411
  * Set the value of an internal option or options.
412
  * Existing options will be overwritten. New options will be added.
413
  * Database options will not be modified.
414
+ *
415
  * @param string|array $key The key of the option to set, or an array of options.
416
  * @param null|mixed $value The value of the option to set.
417
  * @return WPRSS_Help This instance.
427
 
428
  $this->_set_options( $key, $value );
429
  }
430
+
431
 
432
  /**
433
  * Set an option value, or all options.
434
  * In latter case completely overrides the whole options array.
435
+ *
436
  * @param string|array $key The key of the option to set, or the whole options array.
437
  * @param null|mixed $value Value of the option to set.
438
  * @return WPRSS_Help This instance.
446
  $this->_options[ $key ] = $value;
447
  return $this;
448
  }
449
+
450
 
451
  /**
452
  * Returns a WPRSS_Help option or options from the database.
453
+ *
454
  * @param string $key The key of the option to return.
455
  * @param null|mixed $default What to return if option identified by $key is not found.
456
  * @return null|array|mixed The options or option value.
464
 
465
  return isset( $options[ $key ] ) ? $options[ $key ] : $default;
466
  }
467
+
468
 
469
  /**
470
  * Get content of a template.
471
+ *
472
  * Filters used
473
+ *
474
  * - `wprss_help_template_path`
475
  * - `wprss_help_template_vars`
476
+ *
477
  * @param string $path Full path to the template
478
  * @param array $vars This will be passed to the template
479
  */
491
 
492
  return $content;
493
  }
494
+
495
 
496
  /**
497
  * This is called during the `admin_enqueue_scripts` action, and will
498
  * enqueue scripts needed for the backend.
499
+ *
500
  * Filters used:
501
+ *
502
  * - `wprss_help_admin_scripts`
503
+ *
504
  * @return WPRSS_Help This instance.
505
  */
506
  public function _admin_enqueue_scripts() {
507
  $scripts = $this->apply_filters( 'admin_scripts', array(
508
  'jquery-ui-tooltip' => array()
509
  ));
510
+
511
  foreach ( $scripts as $_handle => $_args ) {
512
  // Allows numeric array with handles as values
513
  if ( is_numeric( $_handle ) ) {
514
  $_handle = $_args;
515
  }
516
+
517
  // Allows specifying null as value to simply enqueue handle
518
  if ( empty( $_args ) ){
519
  $_args = array();
520
  }
521
+
522
  array_unshift( $_args, $_handle );
523
  call_user_func_array( 'wp_enqueue_script', $_args );
524
  }
525
+
526
  return $this;
527
  }
528
+
529
+
530
  public function _admin_footer() {
531
  $html = '';
532
  $html .= $this->get_enqueued_tooltip_content_html() . "\n";
533
  $html .= $this->get_admin_footer_js_html();
534
  $html = $this->apply_filters( 'admin_footer', $html );
535
+
536
  echo $html;
537
  }
538
+
539
+
540
  public function is_overrides_default_prefix( $string ) {
541
  return strpos( $string, self::OVERRIDE_DEFAULT_PREFIX ) === 0;
542
  }
543
+
544
+
545
  /**
546
  * @return string This class's text domain
547
  */
548
  public function get_text_domain() {
549
  return self::TEXT_DOMAIN;
550
  }
551
+
552
  /**
553
  * Format this string, replacing placeholders with values, and translate it
554
  * in the class's text domain.
555
+ *
556
  * @see sprintf()
557
  * @param string $string The string to translate.
558
  * @param mixed $argN,.. Additional arguments.
560
  public function __( $string, $argN = null ) {
561
  $args = func_get_args();
562
  $args[0] = $string = __( $string, $this->get_text_domain() );
563
+
564
  $string = call_user_func_array( 'sprintf', $args );
565
+
566
  return $string;
567
  }
568
+
569
  /**
570
  * Hashes all the given values into a single hash.
571
  * Accepts an infinite number of parameters, all of which will be first
572
  * glued together by a separator, then hashed.
573
  * Non-scalar values will be serialized.
574
+ *
575
  * @param mixed $value The value to hash.
576
  * @param mixed $argN Other values to hash.
577
  * @return string The hash.
579
  public function get_hash( $value ) {
580
  $args = func_get_args();
581
  $glue = self::HASHING_CONCATENATOR;
582
+
583
  $blob = '';
584
  foreach ( $args as $_idx => $_arg ) {
585
  $blob .= is_scalar( $_arg ) ? $_arg : serialize( $_arg );
586
  $blob .= $glue;
587
  }
588
+
589
  $blob = substr( $blob, 0, -1 );
590
+
591
  return sha1( $blob );
592
  }
593
+
594
  /**
595
  * Get the class code prefix, or the specified prefixed with it.
596
+ *
597
  * @param string $string A string to prefix.
598
  * @return string The code prefix or the prefixed string.
599
  */
600
  public function get_code_prefix( $string = '' ) {
601
  return self::CODE_PREFIX . (string)$string;
602
  }
603
+
604
  /**
605
  * Optionally prefix a string with the class code prefix, unless it
606
  * contains the "!" character in the very beginning, in which case it will
607
  * simply be removed.
608
+ *
609
  * @param string $string The string to consider for prefixing.
610
  * @return string The prefixed or clean string.
611
  */
614
  ? substr( $string, 1 )
615
  : $this->get_code_prefix( $string );
616
  }
617
+
618
  /**
619
  * Applies filters, but prefixes the filter name with 'wprss_help_',
620
  * unless '!' is specified as the first character of the filter.
621
+ *
622
  * @param string $filter_name Name or "tag" of the filter.
623
  * @param mixed $subject The value to apply filters to.
624
  * @param mixed $argN,.. Additional filter arguments
626
  */
627
  public function apply_filters( $filter_name, $subject, $argN = null ) {
628
  $args = func_get_args();
629
+
630
  $args[0] = $filter_name = $this->prefix( $filter_name );
631
+
632
  return call_user_func_array( 'apply_filters', $args );
633
  }
634
+
635
+
636
  /**
637
  * Applies a filters with the specified name to the options that were
638
  * applied on top of defaults.
639
  * The name will be prefixed with the class prefix 'wprss_help_', and
640
  * suffixed with '_options'.
641
+ *
642
  * @param string $filter_name Name of the filter to apply to the options
643
  * @param array $options The options to filter
644
  * @param mixed $filter_argN,.. Other filter arguments to be passed to filter
645
  */
646
  public function apply_options_filters( $filter_name, $options = array(), $filter_argN = null ) {
647
  $args = func_get_args();
648
+
649
  // Adding sufix
650
  $args[0] = $filter_name .= self::OPTIONS_FILTER_SUFFIX;
651
+
652
  // Applying defaults
653
  $args[1] = $options = $this->get_options( $options );
654
+
655
  // Entry point. Order of args is already correct.
656
  $options = call_user_func_array( array( $this, 'apply_filters' ), $args );
657
+
658
  return $options;
659
  }
660
+
661
+
662
  /**
663
  * Parses the tooltip handle template path for placeholders.
664
+ *
665
  * Filters used:
666
+ *
667
  * - `wprss_help_admin_footer_js_html_template`
668
+ *
669
  * @param null|string $path Optional path to parse and retrieve. Default: value of the 'admin_footer_js_template' option.
670
  * @return string Path to the template.
671
  */
674
  if ( is_null( $path ) ) {
675
  $path = $this->get_options( 'admin_footer_js_template' );
676
  }
677
+
678
  // Entry point
679
  $path = $this->apply_filters( 'admin_footer_js_html_template', $path );
680
+
681
  return $this->parse_path( $path );
682
  }
683
+
684
+
685
  /**
686
  * Get the HTML of the JavaScript for the footer in Admin Panel.
687
+ *
688
  * Filters used:
689
+ *
690
  * - `wprss_help_admin_footer_js_html`
691
+ *
692
  * @param array $options Any additional options to be used with defaults.
693
  * @return string The HTML.
694
  */
695
  public function get_admin_footer_js_html( $options = array() ) {
696
  $options = $this->apply_options_filters( 'admin_footer_js_html', $options);
697
+
698
  $templatePath = $this->get_admin_footer_js_html_template( $options['admin_footer_js_template'] );
699
+
700
  return $this->get_template($templatePath, $options);
701
  }
702
+
703
+
704
  /**
705
  * Parses the tooltip handle template path for placeholders.
706
+ *
707
  * Filters used:
708
+ *
709
  * - `wprss_help_tooltip_handle_html_template`
710
+ *
711
  * @param null|string $path Optional path to parse and retrieve. Default: value of the 'tooltip_handle_template' option.
712
  * @return string Path to the template.
713
  */
716
  if ( is_null( $path ) ) {
717
  $path = $this->get_options( 'tooltip_handle_template' );
718
  }
719
+
720
  // Entry point
721
  $path = $this->apply_filters( 'tooltip_handle_html_template', $path );
722
+
723
  return $this->parse_path( $path );
724
  }
725
+
726
+
727
  /**
728
  * Get the HTML of the tooltip handle.
729
+ *
730
  * Filters used:
731
+ *
732
  * - `wprss_help_tooltip_handle_html_options`
733
+ *
734
  * @param string $text Content of the tooltip text.
735
  * @param string $id ID of the tooltip.
736
  * @param array $options Any additional options to be used with defaults.
742
  // Add template varialbes
743
  $options['tooltip_id'] = $id;
744
  $options['tooltip_text'] = $text;
745
+
746
  $templatePath = $this->get_tooltip_handle_html_template( $options['tooltip_handle_template'] );
747
+
748
  return $this->get_template($templatePath, $options);
749
  }
750
+
751
+
752
  /**
753
  * Parses the tooltip content template path for placeholders.
754
+ *
755
  * Filters used:
756
+ *
757
  * - `wprss_help_tooltip_content_html_template`
758
+ *
759
  * @param null|string $path Optional path to parse and retrieve. Default: value of the 'tooltip_handle_template' option.
760
  * @return string Path to the template.
761
  */
764
  if ( is_null( $path ) ) {
765
  $path = $this->get_options( 'tooltip_content_template' );
766
  }
767
+
768
  // Entry point
769
  $path = $this->apply_filters( 'tooltip_content_html_template', $path );
770
+
771
  return $this->parse_path( $path );
772
  }
773
+
774
+
775
  /**
776
  * Get the HTML of the tooltip content.
777
+ *
778
  * Filters used:
779
+ *
780
  * - `wprss_help_tooltip_content_html_options`
781
+ *
782
  * @param string $text Content of the tooltip text.
783
  * @param string $id ID of the tooltip.
784
  * @param array $options Any additional options to be used with defaults.
786
  */
787
  public function get_tooltip_content_html( $text, $id, $options = array() ) {
788
  $options = $this->apply_options_filters( 'tooltip_content_html', $options, $text, $id );
789
+
790
  // Add template varialbes
791
  $options['tooltip_id'] = $id;
792
  $options['tooltip_text'] = $text;
793
+
794
  $templatePath = $this->get_tooltip_content_html_template( $options['tooltip_content_template'] );
795
+
796
  return $this->get_template( $templatePath, $options );
797
  }
798
+
799
+
800
  /**
801
  * Add tooltip and get tooltip HTML.
802
  * If $text is null, just get the HTML of tooltip with specified ID.
803
  * The `is_enqueue_tooltip_content` option determines whether to enqueue
804
  * the content, instead of outputting it after the handle.
805
+ *
806
  * @param string $id ID for this tooltip
807
  * @param string|null $text Text of this tooltip. If null, tooltip will not be added, but only retrieved.
808
  * @param array|bool $options The options for this operation, or a boolean indicating whether or not content is to be enqueued
812
  $this->add_tooltip( $id, $text, $options );
813
  return $this->do_tooltip( $id );
814
  }
815
+
816
+
817
  /**
818
  * Add tooltips in a batch, with optionally prefixed ID.
819
+ *
820
  * @param array $tooltips An array where key is tooltip ID and value is tooltip text.
821
  * @param string $prefix A prefix to add to all tooltip IDs.
822
  * @param array $options Arra of options for all the tooltips to add.
825
  public function add_tooltips( $tooltips, $prefix = null, $options = array() ) {
826
  $prefix = (string) $prefix;
827
  if ( !is_array($options) ) $options = array();
828
+
829
  foreach ( $tooltips as $_id => $_text ) {
830
  $this->add_tooltip( $prefix . $_id, $_text, $options );
831
  }
832
+
833
  return $this;
834
  }
835
+
836
+
837
  /**
838
  * Add a tooltip for later display.
839
  * Text and options will be replaced by existing text and options, if they
840
  * are empty, and a tooltip with the same ID is already registered.
841
+ *
842
  * @param string $id The ID of this tooltip
843
  * @param string $text Text for this tooltip
844
  * @param array $options Options for this tooltip.
849
  if ( is_null( $text ) ) $text = isset( $tooltip[ self::TOOLTIP_DATA_KEY_TEXT ] ) ? $tooltip[ self::TOOLTIP_DATA_KEY_TEXT ] : $text;
850
  if ( empty( $options ) ) $options = isset( $tooltip[ self::TOOLTIP_DATA_KEY_OPTIONS ] ) ? $tooltip[ self::TOOLTIP_DATA_KEY_OPTIONS ] : $options;
851
  }
852
+
853
  $this->set_tooltip( $id, $text, $options );
854
+
855
  return $this;
856
  }
857
+
858
+
859
  /**
860
  * Set a tooltip, existing or not.
861
+ *
862
  * @param string $id The ID of this tooltip
863
  * @param string $text Text for this tooltip
864
  * @param array $options Options for this tooltip.
870
  self::TOOLTIP_DATA_KEY_TEXT => $text,
871
  self::TOOLTIP_DATA_KEY_OPTIONS => $options
872
  );
873
+
874
  return $this;
875
  }
876
+
877
+
878
  /**
879
  * Retrieve one tooltip, or an array containing all tooltips.
880
+ *
881
  * @param string|null $id The ID of the tooltip to retrieve.
882
  * @param mixed|null $default What to return if tooltip with specified ID not found.
883
  * @return array An array that contains the following indexes: 'id', 'text', 'options'. See {@link add_tooltip()} for details.
886
  if ( is_null( $id ) ) {
887
  return $this->_tooltips;
888
  }
889
+
890
  return $this->has_tooltip( $id ) ? $this->_tooltips[ $id ] : $default;
891
  }
892
+
893
+
894
  /**
895
  * Check whether a tooltip with the specified ID exists.
896
+ *
897
  * @param string $id ID of the tooltip to check for.
898
  * @return boolean True if a tooltip with the specified ID exists; false otherwise.
899
  */
900
  public function has_tooltip( $id ) {
901
  return isset( $this->_tooltips[ $id ] );
902
  }
903
+
904
  /**
905
  * Get registered tooltip HTML.
906
+ *
907
  * Filters used:
908
+ *
909
  * - `wprss_help_tooltip_options` - Filters options used for tooltip
910
+ *
911
  * @param string $id ID for this tooltip
912
  * @param string $text Text of this tooltip
913
  * @param array|bool $options The options for this operation, or a boolean indicating whether or not content is to be enqueued
915
  */
916
  public function do_tooltip( $id ) {
917
  $options = $this->get_options();
918
+
919
  if ( !($tooltip = $this->get_tooltip( $id )) || !isset($tooltip[ self::TOOLTIP_DATA_KEY_TEXT ]) || !$tooltip[ self::TOOLTIP_DATA_KEY_TEXT ] ) {
920
  return isset( $options['tooltip_not_found_handle_html'] )
921
  ? $options['tooltip_not_found_handle_html']
922
  : null;
923
  }
924
+
925
  $options = isset( $tooltip[ self::TOOLTIP_DATA_KEY_OPTIONS ] ) ? $tooltip[ self::TOOLTIP_DATA_KEY_OPTIONS ] : null;
926
  $text = isset( $tooltip[ self::TOOLTIP_DATA_KEY_TEXT ] ) ? $tooltip[ self::TOOLTIP_DATA_KEY_TEXT ] : null;
927
+
928
  if ( !is_array( $options ) ) {
929
  $options = array( 'is_enqueue_tooltip_content' => $options );
930
  }
931
+
932
  // Entry point
933
  $options = $this->apply_options_filters( 'tooltip', $options, $id, $text );
934
+
935
  // Get handle HTML
936
  $output = $this->get_tooltip_handle_html( $text, $id, $options );
937
+
938
  if ( $this->evaluate_boolean( $options['is_enqueue_tooltip_content'] ) ) {
939
  $this->enqueue_tooltip_content($text, $id, $options);
940
  }
941
  else {
942
  $output .= $this->get_tooltip_content_html( $text, $id, $options );
943
  }
944
+
945
  return $output;
946
  }
947
+
948
+
949
  /**
950
  * Enqueue tooltip content to be displayed in another part of the page.
951
+ *
952
  * @param string $text The text of the tooltip content to enqueue.
953
  * @param string $id ID of the tooltip, the content of which to enqueue.
954
  * @param array $options This tooltip's options.
956
  */
957
  public function enqueue_tooltip_content( $text, $id, $options = array() ) {
958
  $queue_method = $this->apply_filters( 'enqueue_tooltip_content_method', array( $this, '_enqueue_tooltip_content' ), $options, $id, $text );
959
+
960
  // "Error handling" WP style
961
  if ( !is_callable( $queue_method ) ) {
962
  return new WP_Error( $this->prefix( 'invalid_queue_method' ), $this->__( 'Could not enqueue tooltip content: the queue method is not a valid callable.' ), array(
966
  'options' => $options
967
  ));
968
  }
969
+
970
  call_user_func_array( $queue_method, array( $text, $id, $options ) );
971
+
972
  return $this;
973
  }
974
+
975
+
976
  public function _enqueue_tooltip_content( $text, $id, $options = array() ) {
977
  $hash = $this->get_hash( $text, $id, $options );
978
  $this->_enqueued_tooltip_content[ $hash ] = array(
980
  self::TOOLTIP_DATA_KEY_ID => $id,
981
  self::TOOLTIP_DATA_KEY_OPTIONS => $options
982
  );
983
+
984
  return $this;
985
  }
986
+
987
+
988
  public function get_enqueued_tooltip_content() {
989
  return $this->_enqueued_tooltip_content;
990
  }
991
+
992
+
993
  public function get_enqueued_tooltip_content_html() {
994
  $output = '';
995
  foreach ( $this->get_enqueued_tooltip_content() as $_hash => $_vars ) {
996
  $options = is_array( $_vars[ self::TOOLTIP_DATA_KEY_OPTIONS ] ) ? $_vars[ self::TOOLTIP_DATA_KEY_OPTIONS ] : array();
997
  $output = $this->get_tooltip_content_html( $_vars[ self::TOOLTIP_DATA_KEY_ID ], $_vars[ self::TOOLTIP_DATA_KEY_ID ], $options );
998
  }
999
+
1000
  echo $output;
1001
  }
1002
+
1003
+
1004
  /**
1005
  * Check whether or not the given value is false.
1006
  * False values are all {@link empty()} values, and also strings 'false' and 'no'.
1007
+ *
1008
  * @param mixed $value The value to check.
1009
  * @return boolean Whether or not the value is considered to be false.
1010
  */
1013
  ? false
1014
  : true;
1015
  }
1016
+
1017
 
1018
  /**
1019
  * Merge two arrays in an intuitive way.
1020
  * Input arrays remain unchanged.
1021
+ *
1022
  * @see http://php.net/manual/en/function.array-merge-recursive.php#92195
1023
  * @param array $array1 The array to merge.
1024
  * @param array $array2 The array to merge into.
1037
 
1038
  return $merged;
1039
  }
1040
+
1041
+
1042
  /**
1043
  * Converts an array to a numeric array.
1044
  * If $map is empty, assumes that the array keys are already in order.
1045
  * If $map is a number, assumes it's the amount of elements to return.
1046
  * If $map is an array, assumes it is the map of intended numeric indexes to their value in the input array.
1047
+ *
1048
  * @param array $array The array to convert to a numeric array
1049
  * @param false|null|array $map The map of the array indexes, or number of array elements to slice, or nothing.
1050
  * @return array The resulting numeric array.
1051
  */
1052
  public function array_to_numeric( $array, $map = null ) {
1053
  $result = array();
1054
+
1055
  // If map is not an array, assume it's an indicator
1056
  if ( !is_array( $map ) ) {
1057
  $array = array_values( $array );
1058
  }
1059
+
1060
  // If map is empty, assume keys are in order
1061
  if ( empty( $map ) ) {
1062
  return $array;
1063
  }
1064
+
1065
  // If map is a number, assume it's the amount of elements to return
1066
  if ( is_numeric( $map ) ) {
1067
  $map = intval( $map );
1068
  return array_slice( $array, 0, $map );
1069
  }
1070
+
1071
  foreach( $map as $_idx => $_key ) {
1072
  $result[ $_idx ] = $array[ $_key ];
1073
  }
1074
+
1075
  return $result;
1076
  }
1077
+
1078
+
1079
  /**
1080
  * Parses the template and replaces placeholders with their values.
1081
  * This function uses {@see sprintf()} to format the template string using
1082
  * the values provided in $data.
1083
  * It is also possible for $data to be an associative array of key-value pairs.
1084
+ * To achieve the same result, a map can be provided, mapping data keys to
1085
  * their placeholder positions.
1086
+ * If no map is provided,
1087
+ *
1088
  * @param string $string The template string.
1089
  * @param array $data The key-value pairs of template data.
1090
  * @param false|null|array $map {@see array_to_numeric()} The template value map.
1095
  array_unshift( $data, $string );
1096
  return call_user_func_array( 'sprintf', $data );
1097
  }
1098
+
1099
+
1100
  /**
1101
  * Parses a path template specifically with WPRSS_Help path placeholders.
1102
+ *
1103
  * Filters used (in order):
1104
+ *
1105
  * 1. `parse_path_data_default`;
1106
  * 2. `parse_path_data`;
1107
  * 3. `parse_path_map`;
1108
  * 4. `parse_path_path`.
1109
+ *
1110
  * @see WPRSS_Help::parse_template()
1111
  * @param string $path The path to parse.
1112
  * @param null|array $data Any additional data. Will be merged with defaults.
1117
  if( is_null( $data ) ) {
1118
  $data = array();
1119
  }
1120
+
1121
  $defaults = $this->apply_filters( 'parse_path_data_default', array(
1122
  'wprss_templates_dir' => wprss_get_templates_dir()
1123
  ));
1125
  $data = $this->apply_filters( 'parse_path_data', $data, $path, $map );
1126
  $map = $this->apply_filters( 'parse_path_map', $map, $data, $path );
1127
  $path = $this->apply_filters( 'parse_path_path', $path, $data, $map );
1128
+
1129
  return $this->parse_template( $path, $data, $map );
1130
  }
1131
  }
includes/admin-log.php CHANGED
@@ -203,6 +203,30 @@
203
  }
204
 
205
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
  /**
207
  * Adds an empty line at the end of the log file.
208
  *
203
  }
204
 
205
 
206
+ /**
207
+ * Downloads the log file.
208
+ *
209
+ * @since 4.7.8
210
+ */
211
+ function wprss_download_log() {
212
+ if ( !file_exists( wprss_log_file() ) ) {
213
+ wprss_clear_log();
214
+ }
215
+ else {
216
+ $file = wprss_log_file();
217
+ header( 'Content-Description: File Transfer' );
218
+ header( 'Content-type: text/plain' );
219
+ header( 'Content-Disposition: attachment; filename="error-log.txt"' );
220
+ header( 'Expires: 0' );
221
+ header( 'Cache-Control: must-revalidate' );
222
+ header( 'Pragma: public' );
223
+ header( 'Content-Length: ' . filesize( $file ) );
224
+ readfile( $file );
225
+ exit;
226
+ }
227
+ }
228
+
229
+
230
  /**
231
  * Adds an empty line at the end of the log file.
232
  *
includes/admin-options.php CHANGED
@@ -258,7 +258,12 @@
258
  );
259
  */
260
 
261
-
 
 
 
 
 
262
 
263
  do_action( 'wprss_admin_init' );
264
  }
258
  );
259
  */
260
 
261
+ //If user requested to download system info, generate the download.
262
+ if ( isset( $_POST['wprss-sysinfo'] ) )
263
+ do_action( 'wprss_download_sysinfo' );
264
+ if ( isset( $_POST['wprss-sysinfo'] ) ) {
265
+ // do_action( 'wprss_download_log' );
266
+ }
267
 
268
  do_action( 'wprss_admin_init' );
269
  }
includes/autoload.php ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ if (!function_exists('wprss_autoloader')) {
4
+ /**
5
+ *
6
+ * @return Aventura\Wprss\Core\Loader The loader singleton instance
7
+ */
8
+ function wprss_autoloader() {
9
+ static $loader = null;
10
+ $className = 'Aventura\\Wprss\\Core\\Loader';
11
+ if (!class_exists($className)){
12
+ $dir = dirname(__FILE__);
13
+ $classPath = str_replace('\\', DIRECTORY_SEPARATOR, $className);
14
+ $classPath = "{$dir}/{$classPath}.php";
15
+ require_once($classPath);
16
+ }
17
+
18
+ if ($loader === null) {
19
+ $loader = new $className();
20
+ /* @var $loader Aventura\Wprss\Core\Loader */
21
+ $loader->register();
22
+ }
23
+
24
+ return $loader;
25
+ }
26
+ }
includes/feed-importing.php CHANGED
@@ -4,14 +4,14 @@
4
  *
5
  * @package WPRSSAggregator
6
  */
7
-
8
 
9
  // Warning: Order may be important
10
  add_filter('wprss_normalize_permalink', 'wprss_google_news_url_fix', 8);
11
  add_filter('wprss_normalize_permalink', 'wprss_bing_news_url_fix', 9);
12
  add_filter('wprss_normalize_permalink', 'wprss_google_alerts_url_fix', 10);
13
  add_filter('wprss_normalize_permalink', 'wprss_convert_video_permalink', 100);
14
-
15
 
16
  add_action( 'wprss_fetch_single_feed_hook', 'wprss_fetch_insert_single_feed_items' );
17
  /**
@@ -40,7 +40,7 @@
40
  delete_post_meta( $feed_ID, 'wprss_force_next_fetch' );
41
  wprss_log( 'Force feed flag removed', null, WPRSS_LOG_LEVEL_SYSTEM );
42
  }
43
-
44
  $start_of_update = wprss_flag_feed_as_updating( $feed_ID );
45
  wprss_log_obj( 'Start of import time updated', date( 'Y-m-d H:i:s', $start_of_update), null, WPRSS_LOG_LEVEL_SYSTEM );
46
 
@@ -53,7 +53,7 @@
53
  // Get the feed limit from post meta
54
  $feed_limit = get_post_meta( $feed_ID, 'wprss_limit', true );
55
  wprss_log_obj( 'Feed limit value is', $feed_limit, null, WPRSS_LOG_LEVEL_SYSTEM );
56
-
57
  // If the feed has no individual limit
58
  if ( $feed_limit === '' || intval( $feed_limit ) <= 0 ) {
59
  wprss_log_obj( 'Using global limit', $feed_limit, null, WPRSS_LOG_LEVEL_NOTICE );
@@ -141,17 +141,17 @@
141
  } else {
142
  wprss_log( 'Items to import remained untouched. Not items already exist or are blacklisted.', null, WPRSS_LOG_LEVEL_SYSTEM );
143
  }
144
-
145
  $items_to_insert = $new_items;
146
 
147
  // If using a limit - delete any excess items to make room for the new items
148
  if ( $feed_limit !== NULL ) {
149
  wprss_log_obj( 'Some items may be deleted due to limit', $feed_limit, null, WPRSS_LOG_LEVEL_SYSTEM );
150
-
151
  // Get the number of feed items in DB, and their count
152
  $db_feed_items = wprss_get_feed_items_for_source( $feed_ID );
153
  $num_db_feed_items = $db_feed_items->post_count;
154
-
155
  // Get the number of feed items we can store until we reach the limit
156
  $num_can_insert = $feed_limit - $num_db_feed_items;
157
  // Calculate how many feed items we must delete before importing, to keep to the limit
@@ -170,11 +170,11 @@
170
  foreach ( $feed_items_to_delete as $key => $post ) {
171
  wp_delete_post( $post->ID, TRUE );
172
  }
173
-
174
  if ( $deleted_items_count = count( $feed_items_to_delete ) )
175
  wprss_log_obj( 'Items deleted due to limit', $deleted_items_count, null, WPRSS_LOG_LEVEL_NOTICE );
176
  }
177
-
178
  update_post_meta( $feed_ID, 'wprss_last_update', $last_update_time = time() );
179
  update_post_meta( $feed_ID, 'wprss_last_update_items', 0 );
180
  wprss_log_obj( 'Last import time updated', $last_update_time, null, WPRSS_LOG_LEVEL_SYSTEM );
@@ -187,9 +187,9 @@
187
  } else {
188
  wprss_log_obj('The feed URL is not valid! Please recheck', $feed_url);
189
  }
190
-
191
  $next_scheduled = get_post_meta( $feed_ID, 'wprss_reschedule_event', TRUE );
192
-
193
  if ( $next_scheduled !== '' ) {
194
  wprss_feed_source_update_start_schedule( $feed_ID );
195
  delete_post_meta( $feed_ID, 'wprss_reschedule_event' );
@@ -219,7 +219,7 @@
219
  // Remove previously added filters and actions
220
  remove_action( 'wp_feed_options', 'wprss_do_not_cache_feeds' );
221
  remove_filter( 'wp_feed_cache_transient_lifetime' , 'wprss_feed_cache_lifetime' );
222
-
223
  if ( !is_wp_error( $feed ) ) {
224
  // Return the items in the feed.
225
  return $feed->get_items();
@@ -260,7 +260,7 @@
260
  $feed->force_feed( TRUE );
261
  }
262
  }
263
-
264
  // Set timeout limit
265
  $fetch_time_limit = wprss_get_feed_fetch_time_limit();
266
  $feed->set_timeout( $fetch_time_limit );
@@ -277,11 +277,11 @@
277
  $feed->strip_htmltags( $tags_to_strip );
278
 
279
  do_action( 'wprss_fetch_feed_before', $feed );
280
-
281
  // Fetch the feed
282
  $feed->init();
283
  $feed->handle_content_type();
284
-
285
  do_action( 'wprss_fetch_feed_after', $feed );
286
 
287
  // Convert the feed error into a WP_Error, if applicable
@@ -312,33 +312,33 @@
312
  // Return the normalized permalink
313
  return $permalink;
314
  }
315
-
316
-
317
  /**
318
  * Extracts the actual URL from a Google News permalink
319
- *
320
  * @param string $permalink The permalink to normalize.
321
  * @since 4.2.3
322
  */
323
  function wprss_google_news_url_fix($permalink) {
324
  return wprss_tracking_url_fix($permalink, '!^(https?:\/\/)?' . preg_quote('news.google.com', '!') . '.*!');
325
  }
326
-
327
-
328
  /**
329
  * Extracts the actual URL from a Google Alerts permalink
330
- *
331
  * @param string $permalink The permalink to normalize.
332
  * @since 4.7.3
333
  */
334
  function wprss_google_alerts_url_fix($permalink) {
335
  return wprss_tracking_url_fix($permalink, '!^(https?:\/\/)?(www\.)?' . preg_quote('google.com/url', '!') . '.*!');
336
  }
337
-
338
 
339
  /**
340
  * Extracts the actual URL from a Bing permalink
341
- *
342
  * @param string $permalink The permalink to normalize.
343
  * @since 4.2.3
344
  */
@@ -355,7 +355,7 @@
355
  * Fixes the issue with equivalent Google News etc. items having
356
  * different URLs, that contain randomly generated GET parameters.
357
  * Example:
358
- *
359
  * http://news.google.com/news/url?sa=t&fd=R&ct2=us&ei=V3e9U6izMMnm1QaB1YHoDA&url=http://abcd...
360
  * http://news.google.com/news/url?sa=t&fd=R&ct2=us&ei=One9U-HQLsTp1Aal-oDQBQ&url=http://abcd...
361
  *
@@ -370,7 +370,7 @@
370
  // Parse the url
371
  $parsed = parse_url( urldecode( html_entity_decode( $permalink ) ) );
372
  $patterns = is_array($patterns) ? $patterns :array($patterns);
373
-
374
  // If parsing failed, return the permalink
375
  if ( $parsed === FALSE || $parsed === NULL ) return $permalink;
376
 
@@ -382,19 +382,19 @@
382
  break;
383
  }
384
  }
385
-
386
  if( !$isMatch ) return $permalink;
387
-
388
  // Check if the url GET query string is present
389
  if ( !isset( $parsed['query'] ) ) return $permalink;
390
-
391
  // Parse the query string
392
  $query = array();
393
  parse_str( $parsed['query'], $query );
394
-
395
  // Check if the url GET parameter is present in the query string
396
  if ( !is_array($query) || !isset( $query[$argName] ) ) return $permalink;
397
-
398
  return urldecode( $query[$argName] );
399
  }
400
 
@@ -415,12 +415,12 @@
415
 
416
  // If video host was found
417
  if ( $found_video_host !== 0 && $found_video_host !== FALSE ) {
418
-
419
  // Get general options
420
  $options = get_option( 'wprss_settings_general' );
421
  // Get the video link option entry, or false if it does not exist
422
  $video_link = ( isset($options['video_link']) )? $options['video_link'] : 'false';
423
-
424
  // If the video link option is true, change the video URL to its repective host's embedded
425
  // video player URL. Otherwise, leave the permalink as is.
426
  if ( strtolower( $video_link ) === 'true' ) {
@@ -454,7 +454,7 @@
454
  function wprss_items_insert_post( $items, $feed_ID ) {
455
  update_post_meta( $feed_ID, 'wprss_feed_is_updating', $update_started_at = time() );
456
  wprss_log_obj( 'Starting import of items for feed ' . $feed_ID, $update_started_at, null, WPRSS_LOG_LEVEL_INFO );
457
-
458
  // Gather the permalinks of existing feed item's related to this feed source
459
  $existing_permalinks = wprss_get_existing_permalinks( $feed_ID );
460
 
@@ -505,7 +505,7 @@
505
  // If the item is not NULL, continue to inserting the feed item post into the DB
506
  if ( $item !== NULL && !is_bool($item) ) {
507
  wprss_log( 'Using core logic', null, WPRSS_LOG_LEVEL_SYSTEM );
508
-
509
  // Get the date and GTM date and normalize if not valid dor not present
510
  $format = 'Y-m-d H:i:s';
511
  $has_date = $item->get_date( 'U' ) ? TRUE : FALSE;
@@ -520,20 +520,20 @@
520
  'post_content' => '',
521
  'post_status' => 'publish',
522
  'post_type' => 'wprss_feed_item',
523
- 'post_date' => $date,
524
  'post_date_gmt' => $date_gmt
525
  ),
526
  $item
527
  );
528
  wprss_log( 'Post data filters applied', null, WPRSS_LOG_LEVEL_SYSTEM );
529
-
530
  if ( defined('ICL_SITEPRESS_VERSION') )
531
  @include_once( WP_PLUGIN_DIR . '/sitepress-multilingual-cms/inc/wpml-api.php' );
532
  if ( defined('ICL_LANGUAGE_CODE') ) {
533
  $_POST['icl_post_language'] = $language_code = ICL_LANGUAGE_CODE;
534
  wprss_log_obj( 'WPML detected. Language code determined', $language_code, null, WPRSS_LOG_LEVEL_SYSTEM );
535
  }
536
-
537
  // Create and insert post object into the DB
538
  $inserted_ID = wp_insert_post( $feed_item );
539
 
@@ -572,7 +572,7 @@
572
  else {
573
  wprss_log( 'Item already exists and will be skipped', null, WPRSS_LOG_LEVEL_NOTICE );
574
  }
575
-
576
  wprss_log_obj( 'Finished importing item', $permalink, null, WPRSS_LOG_LEVEL_INFO );
577
  }
578
 
@@ -591,12 +591,12 @@
591
  function wprss_items_insert_post_meta( $inserted_ID, $item, $feed_ID, $permalink, $enclosure_url ) {
592
  update_post_meta( $inserted_ID, 'wprss_item_permalink', $permalink );
593
  update_post_meta( $inserted_ID, 'wprss_item_enclosure', $enclosure_url );
594
-
595
  $author = $item->get_author();
596
  if ( $author ) {
597
  update_post_meta( $inserted_ID, 'wprss_item_author', $author->get_name() );
598
  }
599
-
600
  update_post_meta( $inserted_ID, 'wprss_feed_id', $feed_ID);
601
  do_action( 'wprss_items_create_post_meta', $inserted_ID, $item, $feed_ID );
602
  }
@@ -662,7 +662,7 @@
662
 
663
  /**
664
  * Runs the above function with parameter FALSE
665
- *
666
  * @since 3.9
667
  */
668
  function wprss_fetch_insert_all_feed_items_from_cron() {
4
  *
5
  * @package WPRSSAggregator
6
  */
7
+
8
 
9
  // Warning: Order may be important
10
  add_filter('wprss_normalize_permalink', 'wprss_google_news_url_fix', 8);
11
  add_filter('wprss_normalize_permalink', 'wprss_bing_news_url_fix', 9);
12
  add_filter('wprss_normalize_permalink', 'wprss_google_alerts_url_fix', 10);
13
  add_filter('wprss_normalize_permalink', 'wprss_convert_video_permalink', 100);
14
+
15
 
16
  add_action( 'wprss_fetch_single_feed_hook', 'wprss_fetch_insert_single_feed_items' );
17
  /**
40
  delete_post_meta( $feed_ID, 'wprss_force_next_fetch' );
41
  wprss_log( 'Force feed flag removed', null, WPRSS_LOG_LEVEL_SYSTEM );
42
  }
43
+
44
  $start_of_update = wprss_flag_feed_as_updating( $feed_ID );
45
  wprss_log_obj( 'Start of import time updated', date( 'Y-m-d H:i:s', $start_of_update), null, WPRSS_LOG_LEVEL_SYSTEM );
46
 
53
  // Get the feed limit from post meta
54
  $feed_limit = get_post_meta( $feed_ID, 'wprss_limit', true );
55
  wprss_log_obj( 'Feed limit value is', $feed_limit, null, WPRSS_LOG_LEVEL_SYSTEM );
56
+
57
  // If the feed has no individual limit
58
  if ( $feed_limit === '' || intval( $feed_limit ) <= 0 ) {
59
  wprss_log_obj( 'Using global limit', $feed_limit, null, WPRSS_LOG_LEVEL_NOTICE );
141
  } else {
142
  wprss_log( 'Items to import remained untouched. Not items already exist or are blacklisted.', null, WPRSS_LOG_LEVEL_SYSTEM );
143
  }
144
+
145
  $items_to_insert = $new_items;
146
 
147
  // If using a limit - delete any excess items to make room for the new items
148
  if ( $feed_limit !== NULL ) {
149
  wprss_log_obj( 'Some items may be deleted due to limit', $feed_limit, null, WPRSS_LOG_LEVEL_SYSTEM );
150
+
151
  // Get the number of feed items in DB, and their count
152
  $db_feed_items = wprss_get_feed_items_for_source( $feed_ID );
153
  $num_db_feed_items = $db_feed_items->post_count;
154
+
155
  // Get the number of feed items we can store until we reach the limit
156
  $num_can_insert = $feed_limit - $num_db_feed_items;
157
  // Calculate how many feed items we must delete before importing, to keep to the limit
170
  foreach ( $feed_items_to_delete as $key => $post ) {
171
  wp_delete_post( $post->ID, TRUE );
172
  }
173
+
174
  if ( $deleted_items_count = count( $feed_items_to_delete ) )
175
  wprss_log_obj( 'Items deleted due to limit', $deleted_items_count, null, WPRSS_LOG_LEVEL_NOTICE );
176
  }
177
+
178
  update_post_meta( $feed_ID, 'wprss_last_update', $last_update_time = time() );
179
  update_post_meta( $feed_ID, 'wprss_last_update_items', 0 );
180
  wprss_log_obj( 'Last import time updated', $last_update_time, null, WPRSS_LOG_LEVEL_SYSTEM );
187
  } else {
188
  wprss_log_obj('The feed URL is not valid! Please recheck', $feed_url);
189
  }
190
+
191
  $next_scheduled = get_post_meta( $feed_ID, 'wprss_reschedule_event', TRUE );
192
+
193
  if ( $next_scheduled !== '' ) {
194
  wprss_feed_source_update_start_schedule( $feed_ID );
195
  delete_post_meta( $feed_ID, 'wprss_reschedule_event' );
219
  // Remove previously added filters and actions
220
  remove_action( 'wp_feed_options', 'wprss_do_not_cache_feeds' );
221
  remove_filter( 'wp_feed_cache_transient_lifetime' , 'wprss_feed_cache_lifetime' );
222
+
223
  if ( !is_wp_error( $feed ) ) {
224
  // Return the items in the feed.
225
  return $feed->get_items();
260
  $feed->force_feed( TRUE );
261
  }
262
  }
263
+
264
  // Set timeout limit
265
  $fetch_time_limit = wprss_get_feed_fetch_time_limit();
266
  $feed->set_timeout( $fetch_time_limit );
277
  $feed->strip_htmltags( $tags_to_strip );
278
 
279
  do_action( 'wprss_fetch_feed_before', $feed );
280
+
281
  // Fetch the feed
282
  $feed->init();
283
  $feed->handle_content_type();
284
+
285
  do_action( 'wprss_fetch_feed_after', $feed );
286
 
287
  // Convert the feed error into a WP_Error, if applicable
312
  // Return the normalized permalink
313
  return $permalink;
314
  }
315
+
316
+
317
  /**
318
  * Extracts the actual URL from a Google News permalink
319
+ *
320
  * @param string $permalink The permalink to normalize.
321
  * @since 4.2.3
322
  */
323
  function wprss_google_news_url_fix($permalink) {
324
  return wprss_tracking_url_fix($permalink, '!^(https?:\/\/)?' . preg_quote('news.google.com', '!') . '.*!');
325
  }
326
+
327
+
328
  /**
329
  * Extracts the actual URL from a Google Alerts permalink
330
+ *
331
  * @param string $permalink The permalink to normalize.
332
  * @since 4.7.3
333
  */
334
  function wprss_google_alerts_url_fix($permalink) {
335
  return wprss_tracking_url_fix($permalink, '!^(https?:\/\/)?(www\.)?' . preg_quote('google.com/url', '!') . '.*!');
336
  }
337
+
338
 
339
  /**
340
  * Extracts the actual URL from a Bing permalink
341
+ *
342
  * @param string $permalink The permalink to normalize.
343
  * @since 4.2.3
344
  */
355
  * Fixes the issue with equivalent Google News etc. items having
356
  * different URLs, that contain randomly generated GET parameters.
357
  * Example:
358
+ *
359
  * http://news.google.com/news/url?sa=t&fd=R&ct2=us&ei=V3e9U6izMMnm1QaB1YHoDA&url=http://abcd...
360
  * http://news.google.com/news/url?sa=t&fd=R&ct2=us&ei=One9U-HQLsTp1Aal-oDQBQ&url=http://abcd...
361
  *
370
  // Parse the url
371
  $parsed = parse_url( urldecode( html_entity_decode( $permalink ) ) );
372
  $patterns = is_array($patterns) ? $patterns :array($patterns);
373
+
374
  // If parsing failed, return the permalink
375
  if ( $parsed === FALSE || $parsed === NULL ) return $permalink;
376
 
382
  break;
383
  }
384
  }
385
+
386
  if( !$isMatch ) return $permalink;
387
+
388
  // Check if the url GET query string is present
389
  if ( !isset( $parsed['query'] ) ) return $permalink;
390
+
391
  // Parse the query string
392
  $query = array();
393
  parse_str( $parsed['query'], $query );
394
+
395
  // Check if the url GET parameter is present in the query string
396
  if ( !is_array($query) || !isset( $query[$argName] ) ) return $permalink;
397
+
398
  return urldecode( $query[$argName] );
399
  }
400
 
415
 
416
  // If video host was found
417
  if ( $found_video_host !== 0 && $found_video_host !== FALSE ) {
418
+
419
  // Get general options
420
  $options = get_option( 'wprss_settings_general' );
421
  // Get the video link option entry, or false if it does not exist
422
  $video_link = ( isset($options['video_link']) )? $options['video_link'] : 'false';
423
+
424
  // If the video link option is true, change the video URL to its repective host's embedded
425
  // video player URL. Otherwise, leave the permalink as is.
426
  if ( strtolower( $video_link ) === 'true' ) {
454
  function wprss_items_insert_post( $items, $feed_ID ) {
455
  update_post_meta( $feed_ID, 'wprss_feed_is_updating', $update_started_at = time() );
456
  wprss_log_obj( 'Starting import of items for feed ' . $feed_ID, $update_started_at, null, WPRSS_LOG_LEVEL_INFO );
457
+
458
  // Gather the permalinks of existing feed item's related to this feed source
459
  $existing_permalinks = wprss_get_existing_permalinks( $feed_ID );
460
 
505
  // If the item is not NULL, continue to inserting the feed item post into the DB
506
  if ( $item !== NULL && !is_bool($item) ) {
507
  wprss_log( 'Using core logic', null, WPRSS_LOG_LEVEL_SYSTEM );
508
+
509
  // Get the date and GTM date and normalize if not valid dor not present
510
  $format = 'Y-m-d H:i:s';
511
  $has_date = $item->get_date( 'U' ) ? TRUE : FALSE;
520
  'post_content' => '',
521
  'post_status' => 'publish',
522
  'post_type' => 'wprss_feed_item',
523
+ 'post_date' => $date,
524
  'post_date_gmt' => $date_gmt
525
  ),
526
  $item
527
  );
528
  wprss_log( 'Post data filters applied', null, WPRSS_LOG_LEVEL_SYSTEM );
529
+
530
  if ( defined('ICL_SITEPRESS_VERSION') )
531
  @include_once( WP_PLUGIN_DIR . '/sitepress-multilingual-cms/inc/wpml-api.php' );
532
  if ( defined('ICL_LANGUAGE_CODE') ) {
533
  $_POST['icl_post_language'] = $language_code = ICL_LANGUAGE_CODE;
534
  wprss_log_obj( 'WPML detected. Language code determined', $language_code, null, WPRSS_LOG_LEVEL_SYSTEM );
535
  }
536
+
537
  // Create and insert post object into the DB
538
  $inserted_ID = wp_insert_post( $feed_item );
539
 
572
  else {
573
  wprss_log( 'Item already exists and will be skipped', null, WPRSS_LOG_LEVEL_NOTICE );
574
  }
575
+
576
  wprss_log_obj( 'Finished importing item', $permalink, null, WPRSS_LOG_LEVEL_INFO );
577
  }
578
 
591
  function wprss_items_insert_post_meta( $inserted_ID, $item, $feed_ID, $permalink, $enclosure_url ) {
592
  update_post_meta( $inserted_ID, 'wprss_item_permalink', $permalink );
593
  update_post_meta( $inserted_ID, 'wprss_item_enclosure', $enclosure_url );
594
+
595
  $author = $item->get_author();
596
  if ( $author ) {
597
  update_post_meta( $inserted_ID, 'wprss_item_author', $author->get_name() );
598
  }
599
+
600
  update_post_meta( $inserted_ID, 'wprss_feed_id', $feed_ID);
601
  do_action( 'wprss_items_create_post_meta', $inserted_ID, $item, $feed_ID );
602
  }
662
 
663
  /**
664
  * Runs the above function with parameter FALSE
665
+ *
666
  * @since 3.9
667
  */
668
  function wprss_fetch_insert_all_feed_items_from_cron() {
includes/feed-processing.php CHANGED
@@ -11,7 +11,7 @@
11
  /**
12
  * Returns whether or not the feed source will forcefully fetch the next fetch,
13
  * ignoring whether or not it is paused or not.
14
- *
15
  * @param $source_id The ID of the feed soruce
16
  * @return boolean
17
  * @since 3.7
@@ -74,13 +74,14 @@
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,
81
- 'orderby' => 'date',
82
- 'order' => 'DESC',
83
- 'meta_query' => array(
 
84
  array(
85
  'key' => 'wprss_feed_id',
86
  'value' => $source_id,
@@ -195,11 +196,11 @@
195
  * Returns the image of the feed.
196
  * The reason this function exists is for add-ons to be able to detect if the plugin core
197
  * supports feed image functionality through a simple function_exists() call.
198
- *
199
  * @param $source_id The ID of the feed source
200
  * @return string The link to the feed image
201
  * @since 1.0
202
- */
203
  function wprss_get_feed_image( $source_id ) {
204
  return get_post_meta( $source_id, 'wprss_feed_image', true );
205
  }
@@ -215,7 +216,7 @@
215
  */
216
  function wprss_updated_feed_source( $post_ID, $post_after, $post_before ) {
217
  // Check if the post is a feed source and is published
218
-
219
  if ( ( $post_after->post_type == 'wprss_feed' ) && ( $post_after->post_status == 'publish' ) ) {
220
 
221
  if ( isset( $_POST['wprss_url'] ) && !empty( $_POST['wprss_url'] ) ) {
@@ -305,7 +306,7 @@
305
  if ( isset( $json['id'] ) ) {
306
  # Generate the final URL for this feed and update the post meta
307
  $final_url = "http://www.facebook.com/feeds/page.php?format=rss20&id=" . $json['id'];
308
- update_post_meta( $post_id, 'wprss_url', $final_url, $url );
309
  }
310
  }
311
  }
@@ -389,7 +390,7 @@
389
  function wprss_flag_feed_as_idle( $feed_ID ) {
390
  delete_post_meta( $feed_ID, 'wprss_feed_is_updating' );
391
  }
392
-
393
 
394
  /**
395
  * Returns whether or not the feed source is updating.
@@ -401,7 +402,7 @@
401
  function wprss_is_feed_source_updating( $id ) {
402
  // Get the 'updating' meta field
403
  $is_updating_meta = get_post_meta( $id, 'wprss_feed_is_updating', TRUE );
404
-
405
  // Check if the feed has the 'updating' meta field set
406
  if ( $is_updating_meta === '' ) {
407
  // If not, then the feed is not updating
@@ -449,7 +450,7 @@
449
  if ( $is_deleting_meta === '' ) {
450
  return FALSE;
451
  }
452
-
453
  $diff = time() - $is_deleting_meta;
454
 
455
  $items = wprss_get_feed_items_for_source( $id );
@@ -476,7 +477,7 @@
476
  /**
477
  * Returns true if the feed item is older than the given timestamp,
478
  * false otherwise;
479
- *
480
  * @since 3.8
481
  */
482
  function wprss_is_feed_item_older_than( $id, $timestamp ) {
@@ -492,7 +493,7 @@
492
 
493
  /**
494
  * Returns the maximum age setting for a feed source.
495
- *
496
  * @since 3.8
497
  */
498
  function wprss_get_max_age_for_feed_source( $source_id ) {
@@ -546,7 +547,7 @@
546
  if ( $feed_items-> have_posts() ) {
547
  // Extend the timeout time limit for the deletion of the feed items
548
  $time_limit = wprss_get_item_import_time_limit();
549
- wprss_log( "Extended execution time limit by {$time_limit}s for imported items truncation.", null, WPRSS_LOG_LEVEL_INFO );
550
  set_time_limit( $time_limit );
551
  // For each feed item
552
  while ( $feed_items->have_posts() ) {
@@ -555,7 +556,7 @@
555
  if ( wprss_is_feed_item_older_than( get_the_ID(), $max_age ) === TRUE ){
556
  // Delete the post
557
  wp_delete_post( get_the_ID(), true );
558
- }
559
  }
560
  // Reset feed items query data
561
  wp_reset_postdata();
@@ -590,7 +591,7 @@
590
  $threshold = $general_settings['limit_feed_items_db'];
591
  $post_types = apply_filters( 'wprss_truncation_post_types', array( 'wprss_feed_item' ) );
592
  $post_types_str = array_map( 'wprss_return_as_string', $post_types );
593
-
594
  $post_type_list = implode( ',' , $post_types_str );
595
 
596
  // Query post type
@@ -601,7 +602,7 @@
601
  AND post_status = 'publish'
602
  ORDER BY post_modified DESC
603
  ";
604
-
605
  $results = $wpdb->get_results( $query );
606
 
607
  // Check if there are any results
@@ -624,8 +625,8 @@
624
  add_filter( 'wprss_insert_post_item_conditionals', 'wprss_check_feed_item_date_on_import', 2, 3 );
625
  /**
626
  * When a feed item is imported, it's date is compared against the max age of it's feed source.
627
- *
628
- *
629
  * @since 3.8
630
  */
631
  function wprss_check_feed_item_date_on_import( $item, $source, $permalink ){
@@ -642,7 +643,7 @@
642
 
643
  // Calculate the age difference
644
  $difference = $age - $max_age;
645
-
646
  if ( $difference <= 0 ) {
647
  return NULL;
648
  } else {
@@ -660,13 +661,13 @@
660
  wp_schedule_single_event( time(), 'wprss_delete_all_feed_items_hook' );
661
  set_transient( WPRSS_TRANSIENT_NAME_IS_REIMPORTING, true );
662
  }
663
-
664
-
665
-
666
  function wprss_schedule_reimport_all($deleted_ids) {
667
  if( !get_transient( WPRSS_TRANSIENT_NAME_IS_REIMPORTING ) )
668
  return;
669
-
670
  wprss_log( 'Re-import scheduled...', __FUNCTION__, WPRSS_LOG_LEVEL_SYSTEM);
671
  delete_transient( WPRSS_TRANSIENT_NAME_IS_REIMPORTING );
672
  wprss_fetch_insert_all_feed_items( TRUE );
11
  /**
12
  * Returns whether or not the feed source will forcefully fetch the next fetch,
13
  * ignoring whether or not it is paused or not.
14
+ *
15
  * @param $source_id The ID of the feed soruce
16
  * @return boolean
17
  * @since 3.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,
81
+ 'ignore_sticky_posts' => 'true',
82
+ 'orderby' => 'date',
83
+ 'order' => 'DESC',
84
+ 'meta_query' => array(
85
  array(
86
  'key' => 'wprss_feed_id',
87
  'value' => $source_id,
196
  * Returns the image of the feed.
197
  * The reason this function exists is for add-ons to be able to detect if the plugin core
198
  * supports feed image functionality through a simple function_exists() call.
199
+ *
200
  * @param $source_id The ID of the feed source
201
  * @return string The link to the feed image
202
  * @since 1.0
203
+ */
204
  function wprss_get_feed_image( $source_id ) {
205
  return get_post_meta( $source_id, 'wprss_feed_image', true );
206
  }
216
  */
217
  function wprss_updated_feed_source( $post_ID, $post_after, $post_before ) {
218
  // Check if the post is a feed source and is published
219
+
220
  if ( ( $post_after->post_type == 'wprss_feed' ) && ( $post_after->post_status == 'publish' ) ) {
221
 
222
  if ( isset( $_POST['wprss_url'] ) && !empty( $_POST['wprss_url'] ) ) {
306
  if ( isset( $json['id'] ) ) {
307
  # Generate the final URL for this feed and update the post meta
308
  $final_url = "http://www.facebook.com/feeds/page.php?format=rss20&id=" . $json['id'];
309
+ update_post_meta( $post_id, 'wprss_url', $final_url, $url );
310
  }
311
  }
312
  }
390
  function wprss_flag_feed_as_idle( $feed_ID ) {
391
  delete_post_meta( $feed_ID, 'wprss_feed_is_updating' );
392
  }
393
+
394
 
395
  /**
396
  * Returns whether or not the feed source is updating.
402
  function wprss_is_feed_source_updating( $id ) {
403
  // Get the 'updating' meta field
404
  $is_updating_meta = get_post_meta( $id, 'wprss_feed_is_updating', TRUE );
405
+
406
  // Check if the feed has the 'updating' meta field set
407
  if ( $is_updating_meta === '' ) {
408
  // If not, then the feed is not updating
450
  if ( $is_deleting_meta === '' ) {
451
  return FALSE;
452
  }
453
+
454
  $diff = time() - $is_deleting_meta;
455
 
456
  $items = wprss_get_feed_items_for_source( $id );
477
  /**
478
  * Returns true if the feed item is older than the given timestamp,
479
  * false otherwise;
480
+ *
481
  * @since 3.8
482
  */
483
  function wprss_is_feed_item_older_than( $id, $timestamp ) {
493
 
494
  /**
495
  * Returns the maximum age setting for a feed source.
496
+ *
497
  * @since 3.8
498
  */
499
  function wprss_get_max_age_for_feed_source( $source_id ) {
547
  if ( $feed_items-> have_posts() ) {
548
  // Extend the timeout time limit for the deletion of the feed items
549
  $time_limit = wprss_get_item_import_time_limit();
550
+ wprss_log( "Extended execution time limit by {$time_limit}s for imported items truncation.", null, WPRSS_LOG_LEVEL_SYSTEM );
551
  set_time_limit( $time_limit );
552
  // For each feed item
553
  while ( $feed_items->have_posts() ) {
556
  if ( wprss_is_feed_item_older_than( get_the_ID(), $max_age ) === TRUE ){
557
  // Delete the post
558
  wp_delete_post( get_the_ID(), true );
559
+ }
560
  }
561
  // Reset feed items query data
562
  wp_reset_postdata();
591
  $threshold = $general_settings['limit_feed_items_db'];
592
  $post_types = apply_filters( 'wprss_truncation_post_types', array( 'wprss_feed_item' ) );
593
  $post_types_str = array_map( 'wprss_return_as_string', $post_types );
594
+
595
  $post_type_list = implode( ',' , $post_types_str );
596
 
597
  // Query post type
602
  AND post_status = 'publish'
603
  ORDER BY post_modified DESC
604
  ";
605
+
606
  $results = $wpdb->get_results( $query );
607
 
608
  // Check if there are any results
625
  add_filter( 'wprss_insert_post_item_conditionals', 'wprss_check_feed_item_date_on_import', 2, 3 );
626
  /**
627
  * When a feed item is imported, it's date is compared against the max age of it's feed source.
628
+ *
629
+ *
630
  * @since 3.8
631
  */
632
  function wprss_check_feed_item_date_on_import( $item, $source, $permalink ){
643
 
644
  // Calculate the age difference
645
  $difference = $age - $max_age;
646
+
647
  if ( $difference <= 0 ) {
648
  return NULL;
649
  } else {
661
  wp_schedule_single_event( time(), 'wprss_delete_all_feed_items_hook' );
662
  set_transient( WPRSS_TRANSIENT_NAME_IS_REIMPORTING, true );
663
  }
664
+
665
+
666
+
667
  function wprss_schedule_reimport_all($deleted_ids) {
668
  if( !get_transient( WPRSS_TRANSIENT_NAME_IS_REIMPORTING ) )
669
  return;
670
+
671
  wprss_log( 'Re-import scheduled...', __FUNCTION__, WPRSS_LOG_LEVEL_SYSTEM);
672
  delete_transient( WPRSS_TRANSIENT_NAME_IS_REIMPORTING );
673
  wprss_fetch_insert_all_feed_items( TRUE );
includes/image-caching.php CHANGED
@@ -20,8 +20,12 @@ class WPRSS_Image_Cache {
20
  * @since 4.6.10
21
  */
22
  public function __construct() {
 
23
  }
24
 
 
 
 
25
 
26
  /**
27
  * Retrieve the singleton instance of this class.
20
  * @since 4.6.10
21
  */
22
  public function __construct() {
23
+ $this->_construct();
24
  }
25
 
26
+ protected function _construct() {
27
+
28
+ }
29
 
30
  /**
31
  * Retrieve the singleton instance of this class.
includes/libraries/browser.php CHANGED
@@ -1,1082 +1,1103 @@
1
  <?php
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2
  /**
3
- * Modified to remove var
4
- * Chris Christoff on 12/26/2012
5
- * Changes: Changes vars to publics
6
- *
7
- * Modified to work for EDD by
8
- * Chris Christoff on 12/23/2012
9
- * Changes: Removed the browser string return and added spacing. Also removed return HTML formatting.
10
- *
11
- * Modified to add formatted User Agent string for EDD System Info by
12
- * Chris Christoff on 12/23/2012
13
- * Changes: Split user string and add formatting so we can print a nicely
14
- * formatted user agent string on the EDD System Info
15
- *
16
- * File: Browser.php
17
- * Author: Chris Schuld (http://chrisschuld.com/)
18
- * Last Modified: August 20th, 2010
19
- * @version 1.9
20
- * @package PegasusPHP
21
- *
22
- * Copyright (C) 2008-2010 Chris Schuld (chris@chrisschuld.com)
23
- *
24
- * This program is free software; you can redistribute it and/or
25
- * modify it under the terms of the GNU General Public License as
26
- * published by the Free Software Foundation; either version 2 of
27
- * the License, or (at your option) any later version.
28
- *
29
- * This program is distributed in the hope that it will be useful,
30
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
31
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32
- * GNU General Public License for more details at:
33
- * http://www.gnu.org/copyleft/gpl.html
34
- *
35
- *
36
- * Typical Usage:
37
- *
38
- * $browser = new Browser();
39
- * if( $browser->getBrowser() == Browser::BROWSER_FIREFOX && $browser->getVersion() >= 2 ) {
40
- * echo 'You have FireFox version 2 or greater';
41
- * }
42
- *
43
- * User Agents Sampled from: http://www.useragentstring.com/
44
- *
45
- * This implementation is based on the original work from Gary White
46
- * http://apptools.com/phptools/browser/
47
  *
48
- * UPDATES:
 
 
 
 
 
 
49
  *
50
- * 2010-08-20 (v1.9):
51
- * + Added MSN Explorer Browser (legacy)
52
- * + Added Bing/MSN Robot (Thanks Rob MacDonald)
53
- * + Added the Android Platform (PLATFORM_ANDROID)
54
- * + Fixed issue with Android 1.6/2.2 (Thanks Tom Hirashima)
55
  *
56
- * 2010-04-27 (v1.8):
57
- * + Added iPad Support
 
 
 
58
  *
59
- * 2010-03-07 (v1.7):
60
- * + *MAJOR* Rebuild (preg_match and other "slow" routine removal(s))
61
- * + Almost allof Gary's original code has been replaced
62
- * + Large PHPUNIT testing environment created to validate new releases and additions
63
- * + Added FreeBSD Platform
64
- * + Added OpenBSD Platform
65
- * + Added NetBSD Platform
66
- * + Added SunOS Platform
67
- * + Added OpenSolaris Platform
68
- * + Added support of the Iceweazel Browser
69
- * + Added isChromeFrame() call to check if chromeframe is in use
70
- * + Moved the Opera check in front of the Firefox check due to legacy Opera User Agents
71
- * + Added the __toString() method (Thanks Deano)
72
  *
73
- * 2009-11-15:
74
- * + Updated the checkes for Firefox
75
- * + Added the NOKIA platform
76
- * + Added Checks for the NOKIA brower(s)
 
77
  *
78
- * 2009-11-08:
79
- * + PHP 5.3 Support
80
- * + Added support for BlackBerry OS and BlackBerry browser
81
- * + Added support for the Opera Mini browser
82
- * + Added additional documenation
83
- * + Added support for isRobot() and isMobile()
84
- * + Added support for Opera version 10
85
- * + Added support for deprecated Netscape Navigator version 9
86
- * + Added support for IceCat
87
- * + Added support for Shiretoko
88
  *
89
- * 2010-04-27 (v1.8):
90
- * + Added iPad Support
 
 
 
91
  *
92
- * 2009-08-18:
93
- * + Updated to support PHP 5.3 - removed all deprecated function calls
94
- * + Updated to remove all double quotes (") -- converted to single quotes (')
 
 
95
  *
96
- * 2009-04-27:
97
- * + Updated the IE check to remove a typo and bug (thanks John)
 
 
 
98
  *
99
- * 2009-04-22:
100
- * + Added detection for GoogleBot
101
- * + Added detection for the W3C Validator.
102
- * + Added detection for Yahoo! Slurp
 
103
  *
104
- * 2009-03-14:
105
- * + Added detection for iPods.
106
- * + Added Platform detection for iPhones
107
- * + Added Platform detection for iPods
 
108
  *
109
- * 2009-02-16: (Rick Hale)
110
- * + Added version detection for Android phones.
 
 
 
111
  *
112
- * 2008-12-09:
113
- * + Removed unused constant
 
 
 
114
  *
115
- * 2008-11-07:
116
- * + Added Google's Chrome to the detection list
117
- * + Added isBrowser(string) to the list of functions special thanks to
118
- * Daniel 'mavrick' Lang for the function concept (http://mavrick.id.au)
 
119
  *
 
 
 
 
 
120
  *
121
- * Gary White noted: "Since browser detection is so unreliable, I am
122
- * no longer maintaining this script. You are free to use and or
123
- * modify/update it as you want, however the author assumes no
124
- * responsibility for the accuracy of the detected values."
 
125
  *
126
- * Anyone experienced with Gary's script might be interested in these notes:
 
 
 
 
 
 
 
 
127
  *
128
- * Added class constants
129
- * Added detection and version detection for Google's Chrome
130
- * Updated the version detection for Amaya
131
- * Updated the version detection for Firefox
132
- * Updated the version detection for Lynx
133
- * Updated the version detection for WebTV
134
- * Updated the version detection for NetPositive
135
- * Updated the version detection for IE
136
- * Updated the version detection for OmniWeb
137
- * Updated the version detection for iCab
138
- * Updated the version detection for Safari
139
- * Updated Safari to remove mobile devices (iPhone)
140
- * Added detection for iPhone
141
- * Added detection for robots
142
- * Added detection for mobile devices
143
- * Added detection for BlackBerry
144
- * Removed Netscape checks (matches heavily with firefox & mozilla)
145
  *
 
146
  */
147
-
148
- class Browser {
149
- public $_agent = '';
150
- public $_browser_name = '';
151
- public $_version = '';
152
- public $_platform = '';
153
- public $_os = '';
154
- public $_is_aol = false;
155
- public $_is_mobile = false;
156
- public $_is_robot = false;
157
- public $_aol_version = '';
158
-
159
- public $BROWSER_UNKNOWN = 'unknown';
160
- public $VERSION_UNKNOWN = 'unknown';
161
-
162
- public $BROWSER_OPERA = 'Opera'; // Http://www.opera.com/
163
- public $BROWSER_OPERA_MINI = 'Opera Mini'; // Http://www.opera.com/mini/
164
- public $BROWSER_WEBTV = 'WebTV'; // Http://www.webtv.net/pc/
165
- public $BROWSER_IE = 'Internet Explorer'; // Http://www.microsoft.com/ie/
166
- public $BROWSER_POCKET_IE = 'Pocket Internet Explorer'; // Http://en.wikipedia.org/wiki/Internet_Explorer_Mobile
167
- public $BROWSER_KONQUEROR = 'Konqueror'; // Http://www.konqueror.org/
168
- public $BROWSER_ICAB = 'iCab'; // Http://www.icab.de/
169
- public $BROWSER_OMNIWEB = 'OmniWeb'; // Http://www.omnigroup.com/applications/omniweb/
170
- public $BROWSER_FIREBIRD = 'Firebird'; // Http://www.ibphoenix.com/
171
- public $BROWSER_FIREFOX = 'Firefox'; // Http://www.mozilla.com/en-US/firefox/firefox.html
172
- public $BROWSER_ICEWEASEL = 'Iceweasel'; // Http://www.geticeweasel.org/
173
- public $BROWSER_SHIRETOKO = 'Shiretoko'; // Http://wiki.mozilla.org/Projects/shiretoko
174
- public $BROWSER_MOZILLA = 'Mozilla'; // Http://www.mozilla.com/en-US/
175
- public $BROWSER_AMAYA = 'Amaya'; // Http://www.w3.org/Amaya/
176
- public $BROWSER_LYNX = 'Lynx'; // Http://en.wikipedia.org/wiki/Lynx
177
- public $BROWSER_SAFARI = 'Safari'; // Http://apple.com
178
- public $BROWSER_IPHONE = 'iPhone'; // Http://apple.com
179
- public $BROWSER_IPOD = 'iPod'; // Http://apple.com
180
- public $BROWSER_IPAD = 'iPad'; // Http://apple.com
181
- public $BROWSER_CHROME = 'Chrome'; // Http://www.google.com/chrome
182
- public $BROWSER_ANDROID = 'Android'; // Http://www.android.com/
183
- public $BROWSER_GOOGLEBOT = 'GoogleBot'; // Http://en.wikipedia.org/wiki/Googlebot
184
- public $BROWSER_SLURP = 'Yahoo! Slurp'; // Http://en.wikipedia.org/wiki/Yahoo!_Slurp
185
- public $BROWSER_W3CVALIDATOR = 'W3C Validator'; // Http://validator.w3.org/
186
- public $BROWSER_BLACKBERRY = 'BlackBerry'; // Http://www.blackberry.com/
187
- public $BROWSER_ICECAT = 'IceCat'; // Http://en.wikipedia.org/wiki/GNU_IceCat
188
- public $BROWSER_NOKIA_S60 = 'Nokia S60 OSS Browser'; // Http://en.wikipedia.org/wiki/Web_Browser_for_S60
189
- public $BROWSER_NOKIA = 'Nokia Browser'; // * all other WAP-based browsers on the Nokia Platform
190
- public $BROWSER_MSN = 'MSN Browser'; // Http://explorer.msn.com/
191
- public $BROWSER_MSNBOT = 'MSN Bot'; // Http://search.msn.com/msnbot.htm
192
- // Http://en.wikipedia.org/wiki/Msnbot (used for Bing as well)
193
-
194
- public $BROWSER_NETSCAPE_NAVIGATOR = 'Netscape Navigator'; // Http://browser.netscape.com/ (DEPRECATED)
195
- public $BROWSER_GALEON = 'Galeon'; // Http://galeon.sourceforge.net/ (DEPRECATED)
196
- public $BROWSER_NETPOSITIVE = 'NetPositive'; // Http://en.wikipedia.org/wiki/NetPositive (DEPRECATED)
197
- public $BROWSER_PHOENIX = 'Phoenix'; // Http://en.wikipedia.org/wiki/History_of_Mozilla_Firefox (DEPRECATED)
198
-
199
- public $PLATFORM_UNKNOWN = 'unknown';
200
- public $PLATFORM_WINDOWS = 'Windows';
201
- public $PLATFORM_WINDOWS_CE = 'Windows CE';
202
- public $PLATFORM_APPLE = 'Apple';
203
- public $PLATFORM_LINUX = 'Linux';
204
- public $PLATFORM_OS2 = 'OS/2';
205
- public $PLATFORM_BEOS = 'BeOS';
206
- public $PLATFORM_IPHONE = 'iPhone';
207
- public $PLATFORM_IPOD = 'iPod';
208
- public $PLATFORM_IPAD = 'iPad';
209
- public $PLATFORM_BLACKBERRY = 'BlackBerry';
210
- public $PLATFORM_NOKIA = 'Nokia';
211
- public $PLATFORM_FREEBSD = 'FreeBSD';
212
- public $PLATFORM_OPENBSD = 'OpenBSD';
213
- public $PLATFORM_NETBSD = 'NetBSD';
214
- public $PLATFORM_SUNOS = 'SunOS';
215
- public $PLATFORM_OPENSOLARIS = 'OpenSolaris';
216
- public $PLATFORM_ANDROID = 'Android';
217
-
218
- public $OPERATING_SYSTEM_UNKNOWN = 'unknown';
219
-
220
- function Browser($useragent="") {
221
- $this->reset();
222
- if( $useragent != "" ) {
223
- $this->setUserAgent($useragent);
224
- }
225
- else {
226
- $this->determine();
227
- }
228
- }
229
-
230
- /**
231
- * Reset all properties
232
- */
233
- function reset() {
234
- $this->_agent = isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : "";
235
- $this->_browser_name = $this->BROWSER_UNKNOWN;
236
- $this->_version = $this->VERSION_UNKNOWN;
237
- $this->_platform = $this->PLATFORM_UNKNOWN;
238
- $this->_os = $this->OPERATING_SYSTEM_UNKNOWN;
239
- $this->_is_aol = false;
240
- $this->_is_mobile = false;
241
- $this->_is_robot = false;
242
- $this->_aol_version = $this->VERSION_UNKNOWN;
243
- }
244
-
245
- /**
246
- * Check to see if the specific browser is valid
247
- * @param string $browserName
248
- * @return True if the browser is the specified browser
249
- */
250
- function isBrowser($browserName) { return( 0 == strcasecmp($this->_browser_name, trim($browserName))); }
251
-
252
- /**
253
- * The name of the browser. All return types are from the class contants
254
- * @return string Name of the browser
255
- */
256
- function getBrowser() { return $this->_browser_name; }
257
- /**
258
- * Set the name of the browser
259
- * @param $browser The name of the Browser
260
- */
261
- function setBrowser($browser) { return $this->_browser_name = $browser; }
262
- /**
263
- * The name of the platform. All return types are from the class contants
264
- * @return string Name of the browser
265
- */
266
- function getPlatform() { return $this->_platform; }
267
- /**
268
- * Set the name of the platform
269
- * @param $platform The name of the Platform
270
- */
271
- function setPlatform($platform) { return $this->_platform = $platform; }
272
- /**
273
- * The version of the browser.
274
- * @return string Version of the browser (will only contain alpha-numeric characters and a period)
275
- */
276
- function getVersion() { return $this->_version; }
277
- /**
278
- * Set the version of the browser
279
- * @param $version The version of the Browser
280
- */
281
- function setVersion($version) { $this->_version = preg_replace('/[^0-9,.,a-z,A-Z-]/','',$version); }
282
- /**
283
- * The version of AOL.
284
- * @return string Version of AOL (will only contain alpha-numeric characters and a period)
285
- */
286
- function getAolVersion() { return $this->_aol_version; }
287
- /**
288
- * Set the version of AOL
289
- * @param $version The version of AOL
290
- */
291
- function setAolVersion($version) { $this->_aol_version = preg_replace('/[^0-9,.,a-z,A-Z]/','',$version); }
292
- /**
293
- * Is the browser from AOL?
294
- * @return boolean True if the browser is from AOL otherwise false
295
- */
296
- function isAol() { return $this->_is_aol; }
297
- /**
298
- * Is the browser from a mobile device?
299
- * @return boolean True if the browser is from a mobile device otherwise false
300
- */
301
- function isMobile() { return $this->_is_mobile; }
302
- /**
303
- * Is the browser from a robot (ex Slurp,GoogleBot)?
304
- * @return boolean True if the browser is from a robot otherwise false
305
- */
306
- function isRobot() { return $this->_is_robot; }
307
- /**
308
- * Set the browser to be from AOL
309
- * @param $isAol
310
- */
311
- function setAol($isAol) { $this->_is_aol = $isAol; }
312
- /**
313
- * Set the Browser to be mobile
314
- * @param boolean $value is the browser a mobile brower or not
315
- */
316
- function setMobile($value=true) { $this->_is_mobile = $value; }
317
- /**
318
- * Set the Browser to be a robot
319
- * @param boolean $value is the browser a robot or not
320
- */
321
- function setRobot($value=true) { $this->_is_robot = $value; }
322
- /**
323
- * Get the user agent value in use to determine the browser
324
- * @return string The user agent from the HTTP header
325
- */
326
- function getUserAgent() { return $this->_agent; }
327
- /**
328
- * Set the user agent value (the construction will use the HTTP header value - this will overwrite it)
329
- * @param $agent_string The value for the User Agent
330
- */
331
- function setUserAgent($agent_string) {
332
- $this->reset();
333
- $this->_agent = $agent_string;
334
- $this->determine();
335
- }
336
- /**
337
- * Used to determine if the browser is actually "chromeframe"
338
- * @since 1.7
339
- * @return boolean True if the browser is using chromeframe
340
- */
341
- function isChromeFrame() {
342
- return( strpos($this->_agent,"chromeframe") !== false );
343
- }
344
- /**
345
- * Returns a formatted string with a summary of the details of the browser.
346
- * @return string formatted string with a summary of the browser
347
- */
348
- function __toString() {
349
  $text1 = $this->getUserAgent(); //grabs the UA (user agent) string
350
- $UAline1 = substr($text1, 0, 32); //the first line we print should only be the first 32 characters of the UA string
351
  $text2 = $this->getUserAgent();//now we grab it again and save it to a string
352
- $towrapUA = str_replace($UAline1, '', $text2);//the rest of the printoff (other than first line) is equivolent
353
  // To the whole string minus the part we printed off. IE
354
  // User Agent: thefirst32charactersfromUAline1
355
  // the rest of it is now stored in
356
  // $text2 to be printed off
357
  // But we need to add spaces before each line that is split other than line 1
358
  $space = '';
359
- for($i = 0; $i < 25; $i++) {
360
- $space .= ' ';
361
  }
362
  // Now we split the remaining string of UA ($text2) into lines that are prefixed by spaces for formatting
363
- $wordwrapped = chunk_split($towrapUA, 32, "\n $space");
364
- return "Platform: {$this->getPlatform()} \n".
365
- "Browser Name: {$this->getBrowser()} \n" .
366
- "Browser Version: {$this->getVersion()} \n" .
367
- "User Agent String: $UAline1 \n\t\t\t " .
368
- "$wordwrapped";
369
- }
370
- /**
371
- * Protected routine to calculate and determine what the browser is in use (including platform)
372
- */
373
- function determine() {
374
- $this->checkPlatform();
375
- $this->checkBrowsers();
376
- $this->checkForAol();
377
- }
378
- /**
379
- * Protected routine to determine the browser type
380
- * @return boolean True if the browser was detected otherwise false
381
- */
382
- function checkBrowsers() {
383
- return (
384
- // Well-known, well-used
385
- // Special Notes:
386
- // (1) Opera must be checked before FireFox due to the odd
387
- // user agents used in some older versions of Opera
388
- // (2) WebTV is strapped onto Internet Explorer so we must
389
- // check for WebTV before IE
390
- // (3) (deprecated) Galeon is based on Firefox and needs to be
391
- // tested before Firefox is tested
392
- // (4) OmniWeb is based on Safari so OmniWeb check must occur
393
- // before Safari
394
- // (5) Netscape 9+ is based on Firefox so Netscape checks
395
- // before FireFox are necessary
396
- $this->checkBrowserWebTv() ||
397
- $this->checkBrowserInternetExplorer() ||
398
- $this->checkBrowserOpera() ||
399
- $this->checkBrowserGaleon() ||
400
- $this->checkBrowserNetscapeNavigator9Plus() ||
401
- $this->checkBrowserFirefox() ||
402
- $this->checkBrowserChrome() ||
403
- $this->checkBrowserOmniWeb() ||
 
404
 
405
- // Common mobile
406
- $this->checkBrowserAndroid() ||
407
- $this->checkBrowseriPad() ||
408
- $this->checkBrowseriPod() ||
409
- $this->checkBrowseriPhone() ||
410
- $this->checkBrowserBlackBerry() ||
411
- $this->checkBrowserNokia() ||
412
 
413
- // Common bots
414
- $this->checkBrowserGoogleBot() ||
415
- $this->checkBrowserMSNBot() ||
416
- $this->checkBrowserSlurp() ||
417
 
418
- // WebKit base check (post mobile and others)
419
- $this->checkBrowserSafari() ||
420
 
421
- // Everyone else
422
- $this->checkBrowserNetPositive() ||
423
- $this->checkBrowserFirebird() ||
424
- $this->checkBrowserKonqueror() ||
425
- $this->checkBrowserIcab() ||
426
- $this->checkBrowserPhoenix() ||
427
- $this->checkBrowserAmaya() ||
428
- $this->checkBrowserLynx() ||
429
 
430
- $this->checkBrowserShiretoko() ||
431
- $this->checkBrowserIceCat() ||
432
- $this->checkBrowserW3CValidator() ||
433
- $this->checkBrowserMozilla() /* Mozilla is such an open standard that you must check it last */
434
- );
435
- }
436
 
437
- /**
438
- * Determine if the user is using a BlackBerry (last updated 1.7)
439
- * @return boolean True if the browser is the BlackBerry browser otherwise false
440
- */
441
- function checkBrowserBlackBerry() {
442
- if( stripos($this->_agent,'blackberry') !== false ) {
443
- $aresult = explode("/",stristr($this->_agent,"BlackBerry"));
444
- $aversion = explode(' ',$aresult[1]);
445
- $this->setVersion($aversion[0]);
446
- $this->_browser_name = $this->BROWSER_BLACKBERRY;
447
- $this->setMobile(true);
448
- return true;
449
- }
450
- return false;
451
- }
 
452
 
453
- /**
454
- * Determine if the user is using an AOL User Agent (last updated 1.7)
455
- * @return boolean True if the browser is from AOL otherwise false
456
- */
457
- function checkForAol() {
458
- $this->setAol(false);
459
- $this->setAolVersion($this->VERSION_UNKNOWN);
 
460
 
461
- if( stripos($this->_agent,'aol') !== false ) {
462
- $aversion = explode(' ',stristr($this->_agent, 'AOL'));
463
- $this->setAol(true);
464
- $this->setAolVersion(preg_replace('/[^0-9\.a-z]/i', '', $aversion[1]));
465
- return true;
466
- }
467
- return false;
468
- }
469
 
470
- /**
471
- * Determine if the browser is the GoogleBot or not (last updated 1.7)
472
- * @return boolean True if the browser is the GoogletBot otherwise false
473
- */
474
- function checkBrowserGoogleBot() {
475
- if( stripos($this->_agent,'googlebot') !== false ) {
476
- $aresult = explode('/',stristr($this->_agent,'googlebot'));
477
- $aversion = explode(' ',$aresult[1]);
478
- $this->setVersion(str_replace(';','',$aversion[0]));
479
- $this->_browser_name = $this->BROWSER_GOOGLEBOT;
480
- $this->setRobot(true);
481
- return true;
482
- }
483
- return false;
484
- }
 
485
 
486
- /**
487
- * Determine if the browser is the MSNBot or not (last updated 1.9)
488
- * @return boolean True if the browser is the MSNBot otherwise false
489
- */
490
- function checkBrowserMSNBot() {
491
- if( stripos($this->_agent,"msnbot") !== false ) {
492
- $aresult = explode("/",stristr($this->_agent,"msnbot"));
493
- $aversion = explode(" ",$aresult[1]);
494
- $this->setVersion(str_replace(";","",$aversion[0]));
495
- $this->_browser_name = $this->BROWSER_MSNBOT;
496
- $this->setRobot(true);
497
- return true;
498
- }
499
- return false;
500
  }
 
 
501
 
502
- /**
503
- * Determine if the browser is the W3C Validator or not (last updated 1.7)
504
- * @return boolean True if the browser is the W3C Validator otherwise false
505
- */
506
- function checkBrowserW3CValidator() {
507
- if( stripos($this->_agent,'W3C-checklink') !== false ) {
508
- $aresult = explode('/',stristr($this->_agent,'W3C-checklink'));
509
- $aversion = explode(' ',$aresult[1]);
510
- $this->setVersion($aversion[0]);
511
- $this->_browser_name = $this->BROWSER_W3CVALIDATOR;
512
- return true;
513
- }
514
- else if( stripos($this->_agent,'W3C_Validator') !== false ) {
515
- // Some of the Validator versions do not delineate w/ a slash - add it back in
516
- $ua = str_replace("W3C_Validator ", "W3C_Validator/", $this->_agent);
517
- $aresult = explode('/',stristr($ua,'W3C_Validator'));
518
- $aversion = explode(' ',$aresult[1]);
519
- $this->setVersion($aversion[0]);
520
- $this->_browser_name = $this->BROWSER_W3CVALIDATOR;
521
- return true;
522
- }
523
- return false;
524
- }
525
 
526
- /**
527
- * Determine if the browser is the Yahoo! Slurp Robot or not (last updated 1.7)
528
- * @return boolean True if the browser is the Yahoo! Slurp Robot otherwise false
529
- */
530
- function checkBrowserSlurp() {
531
- if( stripos($this->_agent,'slurp') !== false ) {
532
- $aresult = explode('/',stristr($this->_agent,'Slurp'));
533
- $aversion = explode(' ',$aresult[1]);
534
- $this->setVersion($aversion[0]);
535
- $this->_browser_name = $this->BROWSER_SLURP;
536
- $this->setRobot(true);
537
- $this->setMobile(false);
538
- return true;
539
- }
540
- return false;
541
- }
 
542
 
543
- /**
544
- * Determine if the browser is Internet Explorer or not (last updated 1.7)
545
- * @return boolean True if the browser is Internet Explorer otherwise false
546
- */
547
- function checkBrowserInternetExplorer() {
 
548
 
549
- // Test for v1 - v1.5 IE
550
- if( stripos($this->_agent,'microsoft internet explorer') !== false ) {
551
- $this->setBrowser($this->BROWSER_IE);
552
- $this->setVersion('1.0');
553
- $aresult = stristr($this->_agent, '/');
554
- if( preg_match('/308|425|426|474|0b1/i', $aresult) ) {
555
- $this->setVersion('1.5');
556
- }
 
 
 
 
 
 
 
 
 
557
  return true;
558
- }
559
- // Test for versions > 1.5
560
- else if( stripos($this->_agent,'msie') !== false && stripos($this->_agent,'opera') === false ) {
561
- // See if the browser is the odd MSN Explorer
562
- if( stripos($this->_agent,'msnb') !== false ) {
563
- $aresult = explode(' ',stristr(str_replace(';','; ',$this->_agent),'MSN'));
564
- $this->setBrowser( $this->BROWSER_MSN );
565
- $this->setVersion(str_replace(array('(',')',';'),'',$aresult[1]));
566
- return true;
567
- }
568
- $aresult = explode(' ',stristr(str_replace(';','; ',$this->_agent),'msie'));
569
- $this->setBrowser( $this->BROWSER_IE );
570
- $this->setVersion(str_replace(array('(',')',';'),'',$aresult[1]));
571
- return true;
572
- }
573
- // Test for Pocket IE
574
- else if( stripos($this->_agent,'mspie') !== false || stripos($this->_agent,'pocket') !== false ) {
575
- $aresult = explode(' ',stristr($this->_agent,'mspie'));
576
- $this->setPlatform( $this->PLATFORM_WINDOWS_CE );
577
- $this->setBrowser( $this->BROWSER_POCKET_IE );
578
- $this->setMobile(true);
579
-
580
- if( stripos($this->_agent,'mspie') !== false ) {
581
- $this->setVersion($aresult[1]);
582
- }
583
- else {
584
- $aversion = explode('/',$this->_agent);
585
- $this->setVersion($aversion[1]);
586
- }
587
- return true;
588
- }
589
- return false;
590
- }
591
 
592
- /**
593
- * Determine if the browser is Opera or not (last updated 1.7)
594
- * @return boolean True if the browser is Opera otherwise false
595
- */
596
- function checkBrowserOpera() {
597
- if( stripos($this->_agent,'opera mini') !== false ) {
598
- $resultant = stristr($this->_agent, 'opera mini');
599
- if( preg_match('/\//',$resultant) ) {
600
- $aresult = explode('/',$resultant);
601
- $aversion = explode(' ',$aresult[1]);
602
- $this->setVersion($aversion[0]);
603
- }
604
- else {
605
- $aversion = explode(' ',stristr($resultant,'opera mini'));
606
- $this->setVersion($aversion[1]);
607
- }
608
- $this->_browser_name = $this->BROWSER_OPERA_MINI;
609
- $this->setMobile(true);
610
- return true;
611
- }
612
- else if( stripos($this->_agent,'opera') !== false ) {
613
- $resultant = stristr($this->_agent, 'opera');
614
- if( preg_match('/Version\/(10.*)$/',$resultant,$matches) ) {
615
- $this->setVersion($matches[1]);
616
- }
617
- else if( preg_match('/\//',$resultant) ) {
618
- $aresult = explode('/',str_replace("("," ",$resultant));
619
- $aversion = explode(' ',$aresult[1]);
620
- $this->setVersion($aversion[0]);
621
- }
622
- else {
623
- $aversion = explode(' ',stristr($resultant,'opera'));
624
- $this->setVersion(isset($aversion[1])?$aversion[1]:"");
625
- }
626
- $this->_browser_name = $this->BROWSER_OPERA;
627
- return true;
628
- }
629
- return false;
630
- }
631
 
632
- /**
633
- * Determine if the browser is Chrome or not (last updated 1.7)
634
- * @return boolean True if the browser is Chrome otherwise false
635
- */
636
- function checkBrowserChrome() {
637
- if( stripos($this->_agent,'Chrome') !== false ) {
638
- $aresult = explode('/',stristr($this->_agent,'Chrome'));
639
- $aversion = explode(' ',$aresult[1]);
640
- $this->setVersion($aversion[0]);
641
- $this->setBrowser($this->BROWSER_CHROME);
642
- return true;
643
- }
644
- return false;
645
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
646
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
647
 
648
- /**
649
- * Determine if the browser is WebTv or not (last updated 1.7)
650
- * @return boolean True if the browser is WebTv otherwise false
651
- */
652
- function checkBrowserWebTv() {
653
- if( stripos($this->_agent,'webtv') !== false ) {
654
- $aresult = explode('/',stristr($this->_agent,'webtv'));
655
- $aversion = explode(' ',$aresult[1]);
656
- $this->setVersion($aversion[0]);
657
- $this->setBrowser($this->BROWSER_WEBTV);
658
- return true;
659
- }
660
- return false;
661
- }
662
 
663
- /**
664
- * Determine if the browser is NetPositive or not (last updated 1.7)
665
- * @return boolean True if the browser is NetPositive otherwise false
666
- */
667
- function checkBrowserNetPositive() {
668
- if( stripos($this->_agent,'NetPositive') !== false ) {
669
- $aresult = explode('/',stristr($this->_agent,'NetPositive'));
670
- $aversion = explode(' ',$aresult[1]);
671
- $this->setVersion(str_replace(array('(',')',';'),'',$aversion[0]));
672
- $this->setBrowser($this->BROWSER_NETPOSITIVE);
673
- return true;
674
- }
675
- return false;
676
- }
 
677
 
678
- /**
679
- * Determine if the browser is Galeon or not (last updated 1.7)
680
- * @return boolean True if the browser is Galeon otherwise false
681
- */
682
- function checkBrowserGaleon() {
683
- if( stripos($this->_agent,'galeon') !== false ) {
684
- $aresult = explode(' ',stristr($this->_agent,'galeon'));
685
- $aversion = explode('/',$aresult[0]);
686
- $this->setVersion($aversion[1]);
687
- $this->setBrowser($this->BROWSER_GALEON);
688
- return true;
689
- }
690
- return false;
691
- }
 
692
 
693
- /**
694
- * Determine if the browser is Konqueror or not (last updated 1.7)
695
- * @return boolean True if the browser is Konqueror otherwise false
696
- */
697
- function checkBrowserKonqueror() {
698
- if( stripos($this->_agent,'Konqueror') !== false ) {
699
- $aresult = explode(' ',stristr($this->_agent,'Konqueror'));
700
- $aversion = explode('/',$aresult[0]);
701
- $this->setVersion($aversion[1]);
702
- $this->setBrowser($this->BROWSER_KONQUEROR);
703
- return true;
704
- }
705
- return false;
706
- }
 
707
 
708
- /**
709
- * Determine if the browser is iCab or not (last updated 1.7)
710
- * @return boolean True if the browser is iCab otherwise false
711
- */
712
- function checkBrowserIcab() {
713
- if( stripos($this->_agent,'icab') !== false ) {
714
- $aversion = explode(' ',stristr(str_replace('/',' ',$this->_agent),'icab'));
715
- $this->setVersion($aversion[1]);
716
- $this->setBrowser($this->BROWSER_ICAB);
717
- return true;
718
- }
719
- return false;
720
- }
 
 
721
 
722
- /**
723
- * Determine if the browser is OmniWeb or not (last updated 1.7)
724
- * @return boolean True if the browser is OmniWeb otherwise false
725
- */
726
- function checkBrowserOmniWeb() {
727
- if( stripos($this->_agent,'omniweb') !== false ) {
728
- $aresult = explode('/',stristr($this->_agent,'omniweb'));
729
- $aversion = explode(' ',isset($aresult[1])?$aresult[1]:"");
730
- $this->setVersion($aversion[0]);
731
- $this->setBrowser($this->BROWSER_OMNIWEB);
732
- return true;
733
- }
734
- return false;
735
- }
736
 
737
- /**
738
- * Determine if the browser is Phoenix or not (last updated 1.7)
739
- * @return boolean True if the browser is Phoenix otherwise false
740
- */
741
- function checkBrowserPhoenix() {
742
- if( stripos($this->_agent,'Phoenix') !== false ) {
743
- $aversion = explode('/',stristr($this->_agent,'Phoenix'));
744
- $this->setVersion($aversion[1]);
745
- $this->setBrowser($this->BROWSER_PHOENIX);
746
- return true;
747
- }
748
- return false;
749
- }
 
 
750
 
751
- /**
752
- * Determine if the browser is Firebird or not (last updated 1.7)
753
- * @return boolean True if the browser is Firebird otherwise false
754
- */
755
- function checkBrowserFirebird() {
756
- if( stripos($this->_agent,'Firebird') !== false ) {
757
- $aversion = explode('/',stristr($this->_agent,'Firebird'));
758
- $this->setVersion($aversion[1]);
759
- $this->setBrowser($this->BROWSER_FIREBIRD);
760
- return true;
761
- }
762
- return false;
763
- }
 
764
 
765
- /**
766
- * Determine if the browser is Netscape Navigator 9+ or not (last updated 1.7)
767
- * NOTE: (http://browser.netscape.com/ - Official support ended on March 1st, 2008)
768
- * @return boolean True if the browser is Netscape Navigator 9+ otherwise false
769
- */
770
- function checkBrowserNetscapeNavigator9Plus() {
771
- if( stripos($this->_agent,'Firefox') !== false && preg_match('/Navigator\/([^ ]*)/i',$this->_agent,$matches) ) {
772
- $this->setVersion($matches[1]);
773
- $this->setBrowser($this->BROWSER_NETSCAPE_NAVIGATOR);
774
- return true;
775
- }
776
- else if( stripos($this->_agent,'Firefox') === false && preg_match('/Netscape6?\/([^ ]*)/i',$this->_agent,$matches) ) {
777
- $this->setVersion($matches[1]);
778
- $this->setBrowser($this->BROWSER_NETSCAPE_NAVIGATOR);
779
- return true;
780
- }
781
- return false;
782
- }
783
 
784
- /**
785
- * Determine if the browser is Shiretoko or not (https://wiki.mozilla.org/Projects/shiretoko) (last updated 1.7)
786
- * @return boolean True if the browser is Shiretoko otherwise false
787
- */
788
- function checkBrowserShiretoko() {
789
- if( stripos($this->_agent,'Mozilla') !== false && preg_match('/Shiretoko\/([^ ]*)/i',$this->_agent,$matches) ) {
790
- $this->setVersion($matches[1]);
791
- $this->setBrowser($this->BROWSER_SHIRETOKO);
792
- return true;
793
- }
794
- return false;
795
- }
 
 
 
 
 
 
796
 
797
- /**
798
- * Determine if the browser is Ice Cat or not (http://en.wikipedia.org/wiki/GNU_IceCat) (last updated 1.7)
799
- * @return boolean True if the browser is Ice Cat otherwise false
800
- */
801
- function checkBrowserIceCat() {
802
- if( stripos($this->_agent,'Mozilla') !== false && preg_match('/IceCat\/([^ ]*)/i',$this->_agent,$matches) ) {
803
- $this->setVersion($matches[1]);
804
- $this->setBrowser($this->BROWSER_ICECAT);
805
- return true;
806
- }
807
- return false;
808
- }
 
809
 
810
- /**
811
- * Determine if the browser is Nokia or not (last updated 1.7)
812
- * @return boolean True if the browser is Nokia otherwise false
813
- */
814
- function checkBrowserNokia() {
815
- if( preg_match("/Nokia([^\/]+)\/([^ SP]+)/i",$this->_agent,$matches) ) {
816
- $this->setVersion($matches[2]);
817
- if( stripos($this->_agent,'Series60') !== false || strpos($this->_agent,'S60') !== false ) {
818
- $this->setBrowser($this->BROWSER_NOKIA_S60);
819
- }
820
- else {
821
- $this->setBrowser( $this->BROWSER_NOKIA );
822
- }
823
- $this->setMobile(true);
824
- return true;
825
- }
826
- return false;
827
- }
828
 
829
- /**
830
- * Determine if the browser is Firefox or not (last updated 1.7)
831
- * @return boolean True if the browser is Firefox otherwise false
832
- */
833
- function checkBrowserFirefox() {
834
- if( stripos($this->_agent,'safari') === false ) {
835
- if( preg_match("/Firefox[\/ \(]([^ ;\)]+)/i",$this->_agent,$matches) ) {
836
- $this->setVersion($matches[1]);
837
- $this->setBrowser($this->BROWSER_FIREFOX);
838
- return true;
839
- }
840
- else if( preg_match("/Firefox$/i",$this->_agent,$matches) ) {
841
- $this->setVersion("");
842
- $this->setBrowser($this->BROWSER_FIREFOX);
843
- return true;
844
- }
845
  }
846
- return false;
847
- }
 
 
 
848
 
849
- /**
850
- * Determine if the browser is Firefox or not (last updated 1.7)
851
- * @return boolean True if the browser is Firefox otherwise false
852
- */
853
- function checkBrowserIceweasel() {
854
- if( stripos($this->_agent,'Iceweasel') !== false ) {
855
- $aresult = explode('/',stristr($this->_agent,'Iceweasel'));
856
- $aversion = explode(' ',$aresult[1]);
857
- $this->setVersion($aversion[0]);
858
- $this->setBrowser($this->BROWSER_ICEWEASEL);
 
 
 
 
859
  return true;
860
  }
861
- return false;
862
  }
863
- /**
864
- * Determine if the browser is Mozilla or not (last updated 1.7)
865
- * @return boolean True if the browser is Mozilla otherwise false
866
- */
867
- function checkBrowserMozilla() {
868
- if( stripos($this->_agent,'mozilla') !== false && preg_match('/rv:[0-9].[0-9][a-b]?/i',$this->_agent) && stripos($this->_agent,'netscape') === false) {
869
- $aversion = explode(' ',stristr($this->_agent,'rv:'));
870
- preg_match('/rv:[0-9].[0-9][a-b]?/i',$this->_agent,$aversion);
871
- $this->setVersion(str_replace('rv:','',$aversion[0]));
872
- $this->setBrowser($this->BROWSER_MOZILLA);
873
- return true;
874
- }
875
- else if( stripos($this->_agent,'mozilla') !== false && preg_match('/rv:[0-9]\.[0-9]/i',$this->_agent) && stripos($this->_agent,'netscape') === false ) {
876
- $aversion = explode('',stristr($this->_agent,'rv:'));
877
- $this->setVersion(str_replace('rv:','',$aversion[0]));
878
- $this->setBrowser($this->BROWSER_MOZILLA);
879
- return true;
880
- }
881
- else if( stripos($this->_agent,'mozilla') !== false && preg_match('/mozilla\/([^ ]*)/i',$this->_agent,$matches) && stripos($this->_agent,'netscape') === false ) {
882
- $this->setVersion($matches[1]);
883
- $this->setBrowser($this->BROWSER_MOZILLA);
884
- return true;
885
- }
886
- return false;
887
- }
888
 
889
- /**
890
- * Determine if the browser is Lynx or not (last updated 1.7)
891
- * @return boolean True if the browser is Lynx otherwise false
892
- */
893
- function checkBrowserLynx() {
894
- if( stripos($this->_agent,'lynx') !== false ) {
895
- $aresult = explode('/',stristr($this->_agent,'Lynx'));
896
- $aversion = explode(' ',(isset($aresult[1])?$aresult[1]:""));
897
- $this->setVersion($aversion[0]);
898
- $this->setBrowser($this->BROWSER_LYNX);
899
- return true;
900
- }
901
- return false;
902
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
903
 
904
- /**
905
- * Determine if the browser is Amaya or not (last updated 1.7)
906
- * @return boolean True if the browser is Amaya otherwise false
907
- */
908
- function checkBrowserAmaya() {
909
- if( stripos($this->_agent,'amaya') !== false ) {
910
- $aresult = explode('/',stristr($this->_agent,'Amaya'));
911
- $aversion = explode(' ',$aresult[1]);
912
- $this->setVersion($aversion[0]);
913
- $this->setBrowser($this->BROWSER_AMAYA);
914
- return true;
915
- }
916
- return false;
917
- }
 
918
 
919
- /**
920
- * Determine if the browser is Safari or not (last updated 1.7)
921
- * @return boolean True if the browser is Safari otherwise false
922
- */
923
- function checkBrowserSafari() {
924
- if( stripos($this->_agent,'Safari') !== false && stripos($this->_agent,'iPhone') === false && stripos($this->_agent,'iPod') === false ) {
925
- $aresult = explode('/',stristr($this->_agent,'Version'));
926
- if( isset($aresult[1]) ) {
927
- $aversion = explode(' ',$aresult[1]);
928
- $this->setVersion($aversion[0]);
929
- }
930
- else {
931
- $this->setVersion($this->VERSION_UNKNOWN);
932
- }
933
- $this->setBrowser($this->BROWSER_SAFARI);
934
- return true;
935
- }
936
- return false;
937
- }
938
 
939
- /**
940
- * Determine if the browser is iPhone or not (last updated 1.7)
941
- * @return boolean True if the browser is iPhone otherwise false
942
- */
943
- function checkBrowseriPhone() {
944
- if( stripos($this->_agent,'iPhone') !== false ) {
945
- $aresult = explode('/',stristr($this->_agent,'Version'));
946
- if( isset($aresult[1]) ) {
947
- $aversion = explode(' ',$aresult[1]);
948
- $this->setVersion($aversion[0]);
949
- }
950
- else {
951
- $this->setVersion($this->VERSION_UNKNOWN);
952
- }
953
- $this->setMobile(true);
954
- $this->setBrowser($this->BROWSER_IPHONE);
955
- return true;
956
- }
957
- return false;
958
- }
959
 
960
- /**
961
- * Determine if the browser is iPod or not (last updated 1.7)
962
- * @return boolean True if the browser is iPod otherwise false
963
- */
964
- function checkBrowseriPad() {
965
- if( stripos($this->_agent,'iPad') !== false ) {
966
- $aresult = explode('/',stristr($this->_agent,'Version'));
967
- if( isset($aresult[1]) ) {
968
- $aversion = explode(' ',$aresult[1]);
969
- $this->setVersion($aversion[0]);
970
- }
971
- else {
972
- $this->setVersion($this->VERSION_UNKNOWN);
973
- }
974
- $this->setMobile(true);
975
- $this->setBrowser($this->BROWSER_IPAD);
976
- return true;
977
- }
978
- return false;
979
- }
980
 
981
- /**
982
- * Determine if the browser is iPod or not (last updated 1.7)
983
- * @return boolean True if the browser is iPod otherwise false
984
- */
985
- function checkBrowseriPod() {
986
- if( stripos($this->_agent,'iPod') !== false ) {
987
- $aresult = explode('/',stristr($this->_agent,'Version'));
988
- if( isset($aresult[1]) ) {
989
- $aversion = explode(' ',$aresult[1]);
990
- $this->setVersion($aversion[0]);
991
- }
992
- else {
993
- $this->setVersion($this->VERSION_UNKNOWN);
994
- }
995
- $this->setMobile(true);
996
- $this->setBrowser($this->BROWSER_IPOD);
997
- return true;
998
- }
999
- return false;
1000
- }
1001
 
1002
- /**
1003
- * Determine if the browser is Android or not (last updated 1.7)
1004
- * @return boolean True if the browser is Android otherwise false
1005
- */
1006
- function checkBrowserAndroid() {
1007
- if( stripos($this->_agent,'Android') !== false ) {
1008
- $aresult = explode(' ',stristr($this->_agent,'Android'));
1009
- if( isset($aresult[1]) ) {
1010
- $aversion = explode(' ',$aresult[1]);
1011
- $this->setVersion($aversion[0]);
1012
- }
1013
- else {
1014
- $this->setVersion($this->VERSION_UNKNOWN);
1015
- }
1016
- $this->setMobile(true);
1017
- $this->setBrowser($this->BROWSER_ANDROID);
1018
- return true;
1019
- }
1020
- return false;
1021
- }
1022
 
1023
- /**
1024
- * Determine the user's platform (last updated 1.7)
1025
- */
1026
- function checkPlatform() {
1027
- if( stripos($this->_agent, 'windows') !== false ) {
1028
- $this->_platform = $this->PLATFORM_WINDOWS;
1029
- }
1030
- else if( stripos($this->_agent, 'iPad') !== false ) {
1031
- $this->_platform = $this->PLATFORM_IPAD;
1032
- }
1033
- else if( stripos($this->_agent, 'iPod') !== false ) {
1034
- $this->_platform = $this->PLATFORM_IPOD;
1035
- }
1036
- else if( stripos($this->_agent, 'iPhone') !== false ) {
1037
- $this->_platform = $this->PLATFORM_IPHONE;
1038
- }
1039
- elseif( stripos($this->_agent, 'mac') !== false ) {
1040
- $this->_platform = $this->PLATFORM_APPLE;
1041
- }
1042
- elseif( stripos($this->_agent, 'android') !== false ) {
1043
- $this->_platform = $this->PLATFORM_ANDROID;
1044
- }
1045
- elseif( stripos($this->_agent, 'linux') !== false ) {
1046
- $this->_platform = $this->PLATFORM_LINUX;
1047
- }
1048
- else if( stripos($this->_agent, 'Nokia') !== false ) {
1049
- $this->_platform = $this->PLATFORM_NOKIA;
1050
- }
1051
- else if( stripos($this->_agent, 'BlackBerry') !== false ) {
1052
- $this->_platform = $this->PLATFORM_BLACKBERRY;
1053
- }
1054
- elseif( stripos($this->_agent,'FreeBSD') !== false ) {
1055
- $this->_platform = $this->PLATFORM_FREEBSD;
1056
- }
1057
- elseif( stripos($this->_agent,'OpenBSD') !== false ) {
1058
- $this->_platform = $this->PLATFORM_OPENBSD;
1059
- }
1060
- elseif( stripos($this->_agent,'NetBSD') !== false ) {
1061
- $this->_platform = $this->PLATFORM_NETBSD;
1062
- }
1063
- elseif( stripos($this->_agent, 'OpenSolaris') !== false ) {
1064
- $this->_platform = $this->PLATFORM_OPENSOLARIS;
1065
- }
1066
- elseif( stripos($this->_agent, 'SunOS') !== false ) {
1067
- $this->_platform = $this->PLATFORM_SUNOS;
1068
- }
1069
- elseif( stripos($this->_agent, 'OS\/2') !== false ) {
1070
- $this->_platform = $this->PLATFORM_OS2;
1071
- }
1072
- elseif( stripos($this->_agent, 'BeOS') !== false ) {
1073
- $this->_platform = $this->PLATFORM_BEOS;
1074
- }
1075
- elseif( stripos($this->_agent, 'win') !== false ) {
1076
- $this->_platform = $this->PLATFORM_WINDOWS;
1077
- }
1078
 
1079
- }
1080
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1081
 
1082
- ?>
 
1
  <?php
2
+
3
+ // Exit if accessed directly
4
+ if ( ! defined( 'ABSPATH' ) ) exit;
5
+
6
+ /**
7
+ * Modified to remove var
8
+ * Chris Christoff on 12/26/2012
9
+ * Changes: Changes vars to publics
10
+ *
11
+ * Modified to work for EDD by
12
+ * Chris Christoff on 12/23/2012
13
+ * Changes: Removed the browser string return and added spacing. Also removed return HTML formatting.
14
+ *
15
+ * Modified to add formatted User Agent string for EDD System Info by
16
+ * Chris Christoff on 12/23/2012
17
+ * Changes: Split user string and add formatting so we can print a nicely
18
+ * formatted user agent string on the EDD System Info
19
+ *
20
+ * File: Browser.php
21
+ * Author: Chris Schuld (http://chrisschuld.com/)
22
+ * Last Modified: August 20th, 2010
23
+ *
24
+ * @version 1.9
25
+ * @package PegasusPHP
26
+ *
27
+ * Copyright (C) 2008-2010 Chris Schuld (chris@chrisschuld.com)
28
+ *
29
+ * This program is free software; you can redistribute it and/or
30
+ * modify it under the terms of the GNU General Public License as
31
+ * published by the Free Software Foundation; either version 2 of
32
+ * the License, or (at your option) any later version.
33
+ *
34
+ * This program is distributed in the hope that it will be useful,
35
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
36
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
37
+ * GNU General Public License for more details at:
38
+ * http://www.gnu.org/copyleft/gpl.html
39
+ *
40
+ *
41
+ * Typical Usage:
42
+ *
43
+ * $browser = new Browser();
44
+ * if( $browser->getBrowser() == Browser::BROWSER_FIREFOX && $browser->getVersion() >= 2 ) {
45
+ * echo 'You have FireFox version 2 or greater';
46
+ * }
47
+ *
48
+ * User Agents Sampled from: http://www.useragentstring.com/
49
+ *
50
+ * This implementation is based on the original work from Gary White
51
+ * http://apptools.com/phptools/browser/
52
+ *
53
+ * UPDATES:
54
+ *
55
+ * 2010-08-20 (v1.9):
56
+ * + Added MSN Explorer Browser (legacy)
57
+ * + Added Bing/MSN Robot (Thanks Rob MacDonald)
58
+ * + Added the Android Platform (PLATFORM_ANDROID)
59
+ * + Fixed issue with Android 1.6/2.2 (Thanks Tom Hirashima)
60
+ *
61
+ * 2010-04-27 (v1.8):
62
+ * + Added iPad Support
63
+ *
64
+ * 2010-03-07 (v1.7):
65
+ * + *MAJOR* Rebuild (preg_match and other "slow" routine removal(s))
66
+ * + Almost allof Gary's original code has been replaced
67
+ * + Large PHPUNIT testing environment created to validate new releases and additions
68
+ * + Added FreeBSD Platform
69
+ * + Added OpenBSD Platform
70
+ * + Added NetBSD Platform
71
+ * + Added SunOS Platform
72
+ * + Added OpenSolaris Platform
73
+ * + Added support of the Iceweazel Browser
74
+ * + Added isChromeFrame() call to check if chromeframe is in use
75
+ * + Moved the Opera check in front of the Firefox check due to legacy Opera User Agents
76
+ * + Added the __toString() method (Thanks Deano)
77
+ *
78
+ * 2009-11-15:
79
+ * + Updated the checkes for Firefox
80
+ * + Added the NOKIA platform
81
+ * + Added Checks for the NOKIA brower(s)
82
+ *
83
+ * 2009-11-08:
84
+ * + PHP 5.3 Support
85
+ * + Added support for BlackBerry OS and BlackBerry browser
86
+ * + Added support for the Opera Mini browser
87
+ * + Added additional documenation
88
+ * + Added support for isRobot() and isMobile()
89
+ * + Added support for Opera version 10
90
+ * + Added support for deprecated Netscape Navigator version 9
91
+ * + Added support for IceCat
92
+ * + Added support for Shiretoko
93
+ *
94
+ * 2010-04-27 (v1.8):
95
+ * + Added iPad Support
96
+ *
97
+ * 2009-08-18:
98
+ * + Updated to support PHP 5.3 - removed all deprecated function calls
99
+ * + Updated to remove all double quotes (") -- converted to single quotes (')
100
+ *
101
+ * 2009-04-27:
102
+ * + Updated the IE check to remove a typo and bug (thanks John)
103
+ *
104
+ * 2009-04-22:
105
+ * + Added detection for GoogleBot
106
+ * + Added detection for the W3C Validator.
107
+ * + Added detection for Yahoo! Slurp
108
+ *
109
+ * 2009-03-14:
110
+ * + Added detection for iPods.
111
+ * + Added Platform detection for iPhones
112
+ * + Added Platform detection for iPods
113
+ *
114
+ * 2009-02-16: (Rick Hale)
115
+ * + Added version detection for Android phones.
116
+ *
117
+ * 2008-12-09:
118
+ * + Removed unused constant
119
+ *
120
+ * 2008-11-07:
121
+ * + Added Google's Chrome to the detection list
122
+ * + Added isBrowser(string) to the list of functions special thanks to
123
+ * Daniel 'mavrick' Lang for the function concept (http://mavrick.id.au)
124
+ *
125
+ *
126
+ * Gary White noted: "Since browser detection is so unreliable, I am
127
+ * no longer maintaining this script. You are free to use and or
128
+ * modify/update it as you want, however the author assumes no
129
+ * responsibility for the accuracy of the detected values."
130
+ *
131
+ * Anyone experienced with Gary's script might be interested in these notes:
132
+ *
133
+ * Added class constants
134
+ * Added detection and version detection for Google's Chrome
135
+ * Updated the version detection for Amaya
136
+ * Updated the version detection for Firefox
137
+ * Updated the version detection for Lynx
138
+ * Updated the version detection for WebTV
139
+ * Updated the version detection for NetPositive
140
+ * Updated the version detection for IE
141
+ * Updated the version detection for OmniWeb
142
+ * Updated the version detection for iCab
143
+ * Updated the version detection for Safari
144
+ * Updated Safari to remove mobile devices (iPhone)
145
+ * Added detection for iPhone
146
+ * Added detection for robots
147
+ * Added detection for mobile devices
148
+ * Added detection for BlackBerry
149
+ * Removed Netscape checks (matches heavily with firefox & mozilla)
150
+ *
151
+ */
152
+
153
+ class Browser {
154
+ public $_agent = '';
155
+ public $_browser_name = '';
156
+ public $_version = '';
157
+ public $_platform = '';
158
+ public $_os = '';
159
+ public $_is_aol = false;
160
+ public $_is_mobile = false;
161
+ public $_is_robot = false;
162
+ public $_aol_version = '';
163
+
164
+ public $BROWSER_UNKNOWN = 'unknown';
165
+ public $VERSION_UNKNOWN = 'unknown';
166
+
167
+ public $BROWSER_OPERA = 'Opera'; // Http://www.opera.com/
168
+ public $BROWSER_OPERA_MINI = 'Opera Mini'; // Http://www.opera.com/mini/
169
+ public $BROWSER_WEBTV = 'WebTV'; // Http://www.webtv.net/pc/
170
+ public $BROWSER_IE = 'Internet Explorer'; // Http://www.microsoft.com/ie/
171
+ public $BROWSER_POCKET_IE = 'Pocket Internet Explorer'; // Http://en.wikipedia.org/wiki/Internet_Explorer_Mobile
172
+ public $BROWSER_KONQUEROR = 'Konqueror'; // Http://www.konqueror.org/
173
+ public $BROWSER_ICAB = 'iCab'; // Http://www.icab.de/
174
+ public $BROWSER_OMNIWEB = 'OmniWeb'; // Http://www.omnigroup.com/applications/omniweb/
175
+ public $BROWSER_FIREBIRD = 'Firebird'; // Http://www.ibphoenix.com/
176
+ public $BROWSER_FIREFOX = 'Firefox'; // Http://www.mozilla.com/en-US/firefox/firefox.html
177
+ public $BROWSER_ICEWEASEL = 'Iceweasel'; // Http://www.geticeweasel.org/
178
+ public $BROWSER_SHIRETOKO = 'Shiretoko'; // Http://wiki.mozilla.org/Projects/shiretoko
179
+ public $BROWSER_MOZILLA = 'Mozilla'; // Http://www.mozilla.com/en-US/
180
+ public $BROWSER_AMAYA = 'Amaya'; // Http://www.w3.org/Amaya/
181
+ public $BROWSER_LYNX = 'Lynx'; // Http://en.wikipedia.org/wiki/Lynx
182
+ public $BROWSER_SAFARI = 'Safari'; // Http://apple.com
183
+ public $BROWSER_IPHONE = 'iPhone'; // Http://apple.com
184
+ public $BROWSER_IPOD = 'iPod'; // Http://apple.com
185
+ public $BROWSER_IPAD = 'iPad'; // Http://apple.com
186
+ public $BROWSER_CHROME = 'Chrome'; // Http://www.google.com/chrome
187
+ public $BROWSER_ANDROID = 'Android'; // Http://www.android.com/
188
+ public $BROWSER_GOOGLEBOT = 'GoogleBot'; // Http://en.wikipedia.org/wiki/Googlebot
189
+ public $BROWSER_SLURP = 'Yahoo! Slurp'; // Http://en.wikipedia.org/wiki/Yahoo!_Slurp
190
+ public $BROWSER_W3CVALIDATOR = 'W3C Validator'; // Http://validator.w3.org/
191
+ public $BROWSER_BLACKBERRY = 'BlackBerry'; // Http://www.blackberry.com/
192
+ public $BROWSER_ICECAT = 'IceCat'; // Http://en.wikipedia.org/wiki/GNU_IceCat
193
+ public $BROWSER_NOKIA_S60 = 'Nokia S60 OSS Browser'; // Http://en.wikipedia.org/wiki/Web_Browser_for_S60
194
+ public $BROWSER_NOKIA = 'Nokia Browser'; // * all other WAP-based browsers on the Nokia Platform
195
+ public $BROWSER_MSN = 'MSN Browser'; // Http://explorer.msn.com/
196
+ public $BROWSER_MSNBOT = 'MSN Bot'; // Http://search.msn.com/msnbot.htm
197
+ // Http://en.wikipedia.org/wiki/Msnbot (used for Bing as well)
198
+
199
+ public $BROWSER_NETSCAPE_NAVIGATOR = 'Netscape Navigator'; // Http://browser.netscape.com/ (DEPRECATED)
200
+ public $BROWSER_GALEON = 'Galeon'; // Http://galeon.sourceforge.net/ (DEPRECATED)
201
+ public $BROWSER_NETPOSITIVE = 'NetPositive'; // Http://en.wikipedia.org/wiki/NetPositive (DEPRECATED)
202
+ public $BROWSER_PHOENIX = 'Phoenix'; // Http://en.wikipedia.org/wiki/History_of_Mozilla_Firefox (DEPRECATED)
203
+
204
+ public $PLATFORM_UNKNOWN = 'unknown';
205
+ public $PLATFORM_WINDOWS = 'Windows';
206
+ public $PLATFORM_WINDOWS_CE = 'Windows CE';
207
+ public $PLATFORM_APPLE = 'Apple';
208
+ public $PLATFORM_LINUX = 'Linux';
209
+ public $PLATFORM_OS2 = 'OS/2';
210
+ public $PLATFORM_BEOS = 'BeOS';
211
+ public $PLATFORM_IPHONE = 'iPhone';
212
+ public $PLATFORM_IPOD = 'iPod';
213
+ public $PLATFORM_IPAD = 'iPad';
214
+ public $PLATFORM_BLACKBERRY = 'BlackBerry';
215
+ public $PLATFORM_NOKIA = 'Nokia';
216
+ public $PLATFORM_FREEBSD = 'FreeBSD';
217
+ public $PLATFORM_OPENBSD = 'OpenBSD';
218
+ public $PLATFORM_NETBSD = 'NetBSD';
219
+ public $PLATFORM_SUNOS = 'SunOS';
220
+ public $PLATFORM_OPENSOLARIS = 'OpenSolaris';
221
+ public $PLATFORM_ANDROID = 'Android';
222
+
223
+ public $OPERATING_SYSTEM_UNKNOWN = 'unknown';
224
+
225
+ function Browser( $useragent="" ) {
226
+ $this->reset();
227
+ if ( $useragent != "" ) {
228
+ $this->setUserAgent( $useragent );
229
+ } else {
230
+ $this->determine();
231
+ }
232
+ }
233
+
234
  /**
235
+ * Reset all properties
236
+ */
237
+ function reset() {
238
+ $this->_agent = isset( $_SERVER['HTTP_USER_AGENT'] ) ? $_SERVER['HTTP_USER_AGENT'] : "";
239
+ $this->_browser_name = $this->BROWSER_UNKNOWN;
240
+ $this->_version = $this->VERSION_UNKNOWN;
241
+ $this->_platform = $this->PLATFORM_UNKNOWN;
242
+ $this->_os = $this->OPERATING_SYSTEM_UNKNOWN;
243
+ $this->_is_aol = false;
244
+ $this->_is_mobile = false;
245
+ $this->_is_robot = false;
246
+ $this->_aol_version = $this->VERSION_UNKNOWN;
247
+ }
248
+
249
+ /**
250
+ * Check to see if the specific browser is valid
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
251
  *
252
+ * @param string $browserName
253
+ * @return True if the browser is the specified browser
254
+ */
255
+ function isBrowser( $browserName ) { return 0 == strcasecmp( $this->_browser_name, trim( $browserName ) ); }
256
+
257
+ /**
258
+ * The name of the browser. All return types are from the class contants
259
  *
260
+ * @return string Name of the browser
261
+ */
262
+ function getBrowser() { return $this->_browser_name; }
263
+ /**
264
+ * Set the name of the browser
265
  *
266
+ * @param unknown $browser The name of the Browser
267
+ */
268
+ function setBrowser( $browser ) { return $this->_browser_name = $browser; }
269
+ /**
270
+ * The name of the platform. All return types are from the class contants
271
  *
272
+ * @return string Name of the browser
273
+ */
274
+ function getPlatform() { return $this->_platform; }
275
+ /**
276
+ * Set the name of the platform
 
 
 
 
 
 
 
 
277
  *
278
+ * @param unknown $platform The name of the Platform
279
+ */
280
+ function setPlatform( $platform ) { return $this->_platform = $platform; }
281
+ /**
282
+ * The version of the browser.
283
  *
284
+ * @return string Version of the browser (will only contain alpha-numeric characters and a period)
285
+ */
286
+ function getVersion() { return $this->_version; }
287
+ /**
288
+ * Set the version of the browser
 
 
 
 
 
289
  *
290
+ * @param unknown $version The version of the Browser
291
+ */
292
+ function setVersion( $version ) { $this->_version = preg_replace( '/[^0-9,.,a-z,A-Z-]/', '', $version ); }
293
+ /**
294
+ * The version of AOL.
295
  *
296
+ * @return string Version of AOL (will only contain alpha-numeric characters and a period)
297
+ */
298
+ function getAolVersion() { return $this->_aol_version; }
299
+ /**
300
+ * Set the version of AOL
301
  *
302
+ * @param unknown $version The version of AOL
303
+ */
304
+ function setAolVersion( $version ) { $this->_aol_version = preg_replace( '/[^0-9,.,a-z,A-Z]/', '', $version ); }
305
+ /**
306
+ * Is the browser from AOL?
307
  *
308
+ * @return boolean True if the browser is from AOL otherwise false
309
+ */
310
+ function isAol() { return $this->_is_aol; }
311
+ /**
312
+ * Is the browser from a mobile device?
313
  *
314
+ * @return boolean True if the browser is from a mobile device otherwise false
315
+ */
316
+ function isMobile() { return $this->_is_mobile; }
317
+ /**
318
+ * Is the browser from a robot (ex Slurp,GoogleBot)?
319
  *
320
+ * @return boolean True if the browser is from a robot otherwise false
321
+ */
322
+ function isRobot() { return $this->_is_robot; }
323
+ /**
324
+ * Set the browser to be from AOL
325
  *
326
+ * @param unknown $isAol
327
+ */
328
+ function setAol( $isAol ) { $this->_is_aol = $isAol; }
329
+ /**
330
+ * Set the Browser to be mobile
331
  *
332
+ * @param boolean $value is the browser a mobile brower or not
333
+ */
334
+ function setMobile( $value=true ) { $this->_is_mobile = $value; }
335
+ /**
336
+ * Set the Browser to be a robot
337
  *
338
+ * @param boolean $value is the browser a robot or not
339
+ */
340
+ function setRobot( $value=true ) { $this->_is_robot = $value; }
341
+ /**
342
+ * Get the user agent value in use to determine the browser
343
  *
344
+ * @return string The user agent from the HTTP header
345
+ */
346
+ function getUserAgent() { return $this->_agent; }
347
+ /**
348
+ * Set the user agent value (the construction will use the HTTP header value - this will overwrite it)
349
  *
350
+ * @param unknown $agent_string The value for the User Agent
351
+ */
352
+ function setUserAgent( $agent_string ) {
353
+ $this->reset();
354
+ $this->_agent = $agent_string;
355
+ $this->determine();
356
+ }
357
+ /**
358
+ * Used to determine if the browser is actually "chromeframe"
359
  *
360
+ * @since 1.7
361
+ * @return boolean True if the browser is using chromeframe
362
+ */
363
+ function isChromeFrame() {
364
+ return strpos( $this->_agent, "chromeframe" ) !== false;
365
+ }
366
+ /**
367
+ * Returns a formatted string with a summary of the details of the browser.
 
 
 
 
 
 
 
 
 
368
  *
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)
395
+ */
396
+ function determine() {
397
+ $this->checkPlatform();
398
+ $this->checkBrowsers();
399
+ $this->checkForAol();
400
+ }
401
+ /**
402
+ * Protected routine to determine the browser type
403
+ *
404
+ * @return boolean True if the browser was detected otherwise false
405
+ */
406
+ function checkBrowsers() {
407
+ return (
408
+ // Well-known, well-used
409
+ // Special Notes:
410
+ // (1) Opera must be checked before FireFox due to the odd
411
+ // user agents used in some older versions of Opera
412
+ // (2) WebTV is strapped onto Internet Explorer so we must
413
+ // check for WebTV before IE
414
+ // (3) (deprecated) Galeon is based on Firefox and needs to be
415
+ // tested before Firefox is tested
416
+ // (4) OmniWeb is based on Safari so OmniWeb check must occur
417
+ // before Safari
418
+ // (5) Netscape 9+ is based on Firefox so Netscape checks
419
+ // before FireFox are necessary
420
+ $this->checkBrowserWebTv() ||
421
+ $this->checkBrowserInternetExplorer() ||
422
+ $this->checkBrowserOpera() ||
423
+ $this->checkBrowserGaleon() ||
424
+ $this->checkBrowserNetscapeNavigator9Plus() ||
425
+ $this->checkBrowserFirefox() ||
426
+ $this->checkBrowserChrome() ||
427
+ $this->checkBrowserOmniWeb() ||
428
 
429
+ // Common mobile
430
+ $this->checkBrowserAndroid() ||
431
+ $this->checkBrowseriPad() ||
432
+ $this->checkBrowseriPod() ||
433
+ $this->checkBrowseriPhone() ||
434
+ $this->checkBrowserBlackBerry() ||
435
+ $this->checkBrowserNokia() ||
436
 
437
+ // Common bots
438
+ $this->checkBrowserGoogleBot() ||
439
+ $this->checkBrowserMSNBot() ||
440
+ $this->checkBrowserSlurp() ||
441
 
442
+ // WebKit base check (post mobile and others)
443
+ $this->checkBrowserSafari() ||
444
 
445
+ // Everyone else
446
+ $this->checkBrowserNetPositive() ||
447
+ $this->checkBrowserFirebird() ||
448
+ $this->checkBrowserKonqueror() ||
449
+ $this->checkBrowserIcab() ||
450
+ $this->checkBrowserPhoenix() ||
451
+ $this->checkBrowserAmaya() ||
452
+ $this->checkBrowserLynx() ||
453
 
454
+ $this->checkBrowserShiretoko() ||
455
+ $this->checkBrowserIceCat() ||
456
+ $this->checkBrowserW3CValidator() ||
457
+ $this->checkBrowserMozilla() /* Mozilla is such an open standard that you must check it last */
458
+ );
459
+ }
460
 
461
+ /**
462
+ * Determine if the user is using a BlackBerry (last updated 1.7)
463
+ *
464
+ * @return boolean True if the browser is the BlackBerry browser otherwise false
465
+ */
466
+ function checkBrowserBlackBerry() {
467
+ if ( stripos( $this->_agent, 'blackberry' ) !== false ) {
468
+ $aresult = explode( "/", stristr( $this->_agent, "BlackBerry" ) );
469
+ $aversion = explode( ' ', $aresult[1] );
470
+ $this->setVersion( $aversion[0] );
471
+ $this->_browser_name = $this->BROWSER_BLACKBERRY;
472
+ $this->setMobile( true );
473
+ return true;
474
+ }
475
+ return false;
476
+ }
477
 
478
+ /**
479
+ * Determine if the user is using an AOL User Agent (last updated 1.7)
480
+ *
481
+ * @return boolean True if the browser is from AOL otherwise false
482
+ */
483
+ function checkForAol() {
484
+ $this->setAol( false );
485
+ $this->setAolVersion( $this->VERSION_UNKNOWN );
486
 
487
+ if ( stripos( $this->_agent, 'aol' ) !== false ) {
488
+ $aversion = explode( ' ', stristr( $this->_agent, 'AOL' ) );
489
+ $this->setAol( true );
490
+ $this->setAolVersion( preg_replace( '/[^0-9\.a-z]/i', '', $aversion[1] ) );
491
+ return true;
492
+ }
493
+ return false;
494
+ }
495
 
496
+ /**
497
+ * Determine if the browser is the GoogleBot or not (last updated 1.7)
498
+ *
499
+ * @return boolean True if the browser is the GoogletBot otherwise false
500
+ */
501
+ function checkBrowserGoogleBot() {
502
+ if ( stripos( $this->_agent, 'googlebot' ) !== false ) {
503
+ $aresult = explode( '/', stristr( $this->_agent, 'googlebot' ) );
504
+ $aversion = explode( ' ', $aresult[1] );
505
+ $this->setVersion( str_replace( ';', '', $aversion[0] ) );
506
+ $this->_browser_name = $this->BROWSER_GOOGLEBOT;
507
+ $this->setRobot( true );
508
+ return true;
509
+ }
510
+ return false;
511
+ }
512
 
513
+ /**
514
+ * Determine if the browser is the MSNBot or not (last updated 1.9)
515
+ *
516
+ * @return boolean True if the browser is the MSNBot otherwise false
517
+ */
518
+ function checkBrowserMSNBot() {
519
+ if ( stripos( $this->_agent, "msnbot" ) !== false ) {
520
+ $aresult = explode( "/", stristr( $this->_agent, "msnbot" ) );
521
+ $aversion = explode( " ", $aresult[1] );
522
+ $this->setVersion( str_replace( ";", "", $aversion[0] ) );
523
+ $this->_browser_name = $this->BROWSER_MSNBOT;
524
+ $this->setRobot( true );
525
+ return true;
 
526
  }
527
+ return false;
528
+ }
529
 
530
+ /**
531
+ * Determine if the browser is the W3C Validator or not (last updated 1.7)
532
+ *
533
+ * @return boolean True if the browser is the W3C Validator otherwise false
534
+ */
535
+ function checkBrowserW3CValidator() {
536
+ if ( stripos( $this->_agent, 'W3C-checklink' ) !== false ) {
537
+ $aresult = explode( '/', stristr( $this->_agent, 'W3C-checklink' ) );
538
+ $aversion = explode( ' ', $aresult[1] );
539
+ $this->setVersion( $aversion[0] );
540
+ $this->_browser_name = $this->BROWSER_W3CVALIDATOR;
541
+ return true;
542
+ } else if ( stripos( $this->_agent, 'W3C_Validator' ) !== false ) {
543
+ // Some of the Validator versions do not delineate w/ a slash - add it back in
544
+ $ua = str_replace( "W3C_Validator ", "W3C_Validator/", $this->_agent );
545
+ $aresult = explode( '/', stristr( $ua, 'W3C_Validator' ) );
546
+ $aversion = explode( ' ', $aresult[1] );
547
+ $this->setVersion( $aversion[0] );
548
+ $this->_browser_name = $this->BROWSER_W3CVALIDATOR;
549
+ return true;
550
+ }
551
+ return false;
552
+ }
553
 
554
+ /**
555
+ * Determine if the browser is the Yahoo! Slurp Robot or not (last updated 1.7)
556
+ *
557
+ * @return boolean True if the browser is the Yahoo! Slurp Robot otherwise false
558
+ */
559
+ function checkBrowserSlurp() {
560
+ if ( stripos( $this->_agent, 'slurp' ) !== false ) {
561
+ $aresult = explode( '/', stristr( $this->_agent, 'Slurp' ) );
562
+ $aversion = explode( ' ', $aresult[1] );
563
+ $this->setVersion( $aversion[0] );
564
+ $this->_browser_name = $this->BROWSER_SLURP;
565
+ $this->setRobot( true );
566
+ $this->setMobile( false );
567
+ return true;
568
+ }
569
+ return false;
570
+ }
571
 
572
+ /**
573
+ * Determine if the browser is Internet Explorer or not (last updated 1.7)
574
+ *
575
+ * @return boolean True if the browser is Internet Explorer otherwise false
576
+ */
577
+ function checkBrowserInternetExplorer() {
578
 
579
+ // Test for v1 - v1.5 IE
580
+ if ( stripos( $this->_agent, 'microsoft internet explorer' ) !== false ) {
581
+ $this->setBrowser( $this->BROWSER_IE );
582
+ $this->setVersion( '1.0' );
583
+ $aresult = stristr( $this->_agent, '/' );
584
+ if ( preg_match( '/308|425|426|474|0b1/i', $aresult ) ) {
585
+ $this->setVersion( '1.5' );
586
+ }
587
+ return true;
588
+ }
589
+ // Test for versions > 1.5
590
+ else if ( stripos( $this->_agent, 'msie' ) !== false && stripos( $this->_agent, 'opera' ) === false ) {
591
+ // See if the browser is the odd MSN Explorer
592
+ if ( stripos( $this->_agent, 'msnb' ) !== false ) {
593
+ $aresult = explode( ' ', stristr( str_replace( ';', '; ', $this->_agent ), 'MSN' ) );
594
+ $this->setBrowser( $this->BROWSER_MSN );
595
+ $this->setVersion( str_replace( array( '(', ')', ';' ), '', $aresult[1] ) );
596
  return true;
597
+ }
598
+ $aresult = explode( ' ', stristr( str_replace( ';', '; ', $this->_agent ), 'msie' ) );
599
+ $this->setBrowser( $this->BROWSER_IE );
600
+ $this->setVersion( str_replace( array( '(', ')', ';' ), '', $aresult[1] ) );
601
+ return true;
602
+ }
603
+ // Test for Pocket IE
604
+ else if ( stripos( $this->_agent, 'mspie' ) !== false || stripos( $this->_agent, 'pocket' ) !== false ) {
605
+ $aresult = explode( ' ', stristr( $this->_agent, 'mspie' ) );
606
+ $this->setPlatform( $this->PLATFORM_WINDOWS_CE );
607
+ $this->setBrowser( $this->BROWSER_POCKET_IE );
608
+ $this->setMobile( true );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
609
 
610
+ if ( stripos( $this->_agent, 'mspie' ) !== false ) {
611
+ $this->setVersion( $aresult[1] );
612
+ } else {
613
+ $aversion = explode( '/', $this->_agent );
614
+ $this->setVersion( $aversion[1] );
615
+ }
616
+ return true;
617
+ }
618
+ return false;
619
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
620
 
621
+ /**
622
+ * Determine if the browser is Opera or not (last updated 1.7)
623
+ *
624
+ * @return boolean True if the browser is Opera otherwise false
625
+ */
626
+ function checkBrowserOpera() {
627
+ if ( stripos( $this->_agent, 'opera mini' ) !== false ) {
628
+ $resultant = stristr( $this->_agent, 'opera mini' );
629
+ if ( preg_match( '/\//', $resultant ) ) {
630
+ $aresult = explode( '/', $resultant );
631
+ $aversion = explode( ' ', $aresult[1] );
632
+ $this->setVersion( $aversion[0] );
633
+ } else {
634
+ $aversion = explode( ' ', stristr( $resultant, 'opera mini' ) );
635
+ $this->setVersion( $aversion[1] );
636
+ }
637
+ $this->_browser_name = $this->BROWSER_OPERA_MINI;
638
+ $this->setMobile( true );
639
+ return true;
640
+ } else if ( stripos( $this->_agent, 'opera' ) !== false ) {
641
+ $resultant = stristr( $this->_agent, 'opera' );
642
+ if ( preg_match( '/Version\/(10.*)$/', $resultant, $matches ) ) {
643
+ $this->setVersion( $matches[1] );
644
+ } else if ( preg_match( '/\//', $resultant ) ) {
645
+ $aresult = explode( '/', str_replace( "(", " ", $resultant ) );
646
+ $aversion = explode( ' ', $aresult[1] );
647
+ $this->setVersion( $aversion[0] );
648
+ } else {
649
+ $aversion = explode( ' ', stristr( $resultant, 'opera' ) );
650
+ $this->setVersion( isset( $aversion[1] )?$aversion[1]:"" );
651
+ }
652
+ $this->_browser_name = $this->BROWSER_OPERA;
653
+ return true;
654
+ }
655
+ return false;
656
+ }
657
 
658
+ /**
659
+ * Determine if the browser is Chrome or not (last updated 1.7)
660
+ *
661
+ * @return boolean True if the browser is Chrome otherwise false
662
+ */
663
+ function checkBrowserChrome() {
664
+ if ( stripos( $this->_agent, 'Chrome' ) !== false ) {
665
+ $aresult = explode( '/', stristr( $this->_agent, 'Chrome' ) );
666
+ $aversion = explode( ' ', $aresult[1] );
667
+ $this->setVersion( $aversion[0] );
668
+ $this->setBrowser( $this->BROWSER_CHROME );
669
+ return true;
670
+ }
671
+ return false;
672
+ }
673
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
674
 
675
+ /**
676
+ * Determine if the browser is WebTv or not (last updated 1.7)
677
+ *
678
+ * @return boolean True if the browser is WebTv otherwise false
679
+ */
680
+ function checkBrowserWebTv() {
681
+ if ( stripos( $this->_agent, 'webtv' ) !== false ) {
682
+ $aresult = explode( '/', stristr( $this->_agent, 'webtv' ) );
683
+ $aversion = explode( ' ', $aresult[1] );
684
+ $this->setVersion( $aversion[0] );
685
+ $this->setBrowser( $this->BROWSER_WEBTV );
686
+ return true;
687
+ }
688
+ return false;
689
+ }
690
 
691
+ /**
692
+ * Determine if the browser is NetPositive or not (last updated 1.7)
693
+ *
694
+ * @return boolean True if the browser is NetPositive otherwise false
695
+ */
696
+ function checkBrowserNetPositive() {
697
+ if ( stripos( $this->_agent, 'NetPositive' ) !== false ) {
698
+ $aresult = explode( '/', stristr( $this->_agent, 'NetPositive' ) );
699
+ $aversion = explode( ' ', $aresult[1] );
700
+ $this->setVersion( str_replace( array( '(', ')', ';' ), '', $aversion[0] ) );
701
+ $this->setBrowser( $this->BROWSER_NETPOSITIVE );
702
+ return true;
703
+ }
704
+ return false;
705
+ }
706
 
707
+ /**
708
+ * Determine if the browser is Galeon or not (last updated 1.7)
709
+ *
710
+ * @return boolean True if the browser is Galeon otherwise false
711
+ */
712
+ function checkBrowserGaleon() {
713
+ if ( stripos( $this->_agent, 'galeon' ) !== false ) {
714
+ $aresult = explode( ' ', stristr( $this->_agent, 'galeon' ) );
715
+ $aversion = explode( '/', $aresult[0] );
716
+ $this->setVersion( $aversion[1] );
717
+ $this->setBrowser( $this->BROWSER_GALEON );
718
+ return true;
719
+ }
720
+ return false;
721
+ }
722
 
723
+ /**
724
+ * Determine if the browser is Konqueror or not (last updated 1.7)
725
+ *
726
+ * @return boolean True if the browser is Konqueror otherwise false
727
+ */
728
+ function checkBrowserKonqueror() {
729
+ if ( stripos( $this->_agent, 'Konqueror' ) !== false ) {
730
+ $aresult = explode( ' ', stristr( $this->_agent, 'Konqueror' ) );
731
+ $aversion = explode( '/', $aresult[0] );
732
+ $this->setVersion( $aversion[1] );
733
+ $this->setBrowser( $this->BROWSER_KONQUEROR );
734
+ return true;
735
+ }
736
+ return false;
737
+ }
738
 
739
+ /**
740
+ * Determine if the browser is iCab or not (last updated 1.7)
741
+ *
742
+ * @return boolean True if the browser is iCab otherwise false
743
+ */
744
+ function checkBrowserIcab() {
745
+ if ( stripos( $this->_agent, 'icab' ) !== false ) {
746
+ $aversion = explode( ' ', stristr( str_replace( '/', ' ', $this->_agent ), 'icab' ) );
747
+ $this->setVersion( $aversion[1] );
748
+ $this->setBrowser( $this->BROWSER_ICAB );
749
+ return true;
750
+ }
751
+ return false;
752
+ }
753
 
754
+ /**
755
+ * Determine if the browser is OmniWeb or not (last updated 1.7)
756
+ *
757
+ * @return boolean True if the browser is OmniWeb otherwise false
758
+ */
759
+ function checkBrowserOmniWeb() {
760
+ if ( stripos( $this->_agent, 'omniweb' ) !== false ) {
761
+ $aresult = explode( '/', stristr( $this->_agent, 'omniweb' ) );
762
+ $aversion = explode( ' ', isset( $aresult[1] )?$aresult[1]:"" );
763
+ $this->setVersion( $aversion[0] );
764
+ $this->setBrowser( $this->BROWSER_OMNIWEB );
765
+ return true;
766
+ }
767
+ return false;
768
+ }
769
 
770
+ /**
771
+ * Determine if the browser is Phoenix or not (last updated 1.7)
772
+ *
773
+ * @return boolean True if the browser is Phoenix otherwise false
774
+ */
775
+ function checkBrowserPhoenix() {
776
+ if ( stripos( $this->_agent, 'Phoenix' ) !== false ) {
777
+ $aversion = explode( '/', stristr( $this->_agent, 'Phoenix' ) );
778
+ $this->setVersion( $aversion[1] );
779
+ $this->setBrowser( $this->BROWSER_PHOENIX );
780
+ return true;
781
+ }
782
+ return false;
783
+ }
784
 
785
+ /**
786
+ * Determine if the browser is Firebird or not (last updated 1.7)
787
+ *
788
+ * @return boolean True if the browser is Firebird otherwise false
789
+ */
790
+ function checkBrowserFirebird() {
791
+ if ( stripos( $this->_agent, 'Firebird' ) !== false ) {
792
+ $aversion = explode( '/', stristr( $this->_agent, 'Firebird' ) );
793
+ $this->setVersion( $aversion[1] );
794
+ $this->setBrowser( $this->BROWSER_FIREBIRD );
795
+ return true;
796
+ }
797
+ return false;
798
+ }
 
 
 
 
799
 
800
+ /**
801
+ * Determine if the browser is Netscape Navigator 9+ or not (last updated 1.7)
802
+ * NOTE: (http://browser.netscape.com/ - Official support ended on March 1st, 2008)
803
+ *
804
+ * @return boolean True if the browser is Netscape Navigator 9+ otherwise false
805
+ */
806
+ function checkBrowserNetscapeNavigator9Plus() {
807
+ if ( stripos( $this->_agent, 'Firefox' ) !== false && preg_match( '/Navigator\/([^ ]*)/i', $this->_agent, $matches ) ) {
808
+ $this->setVersion( $matches[1] );
809
+ $this->setBrowser( $this->BROWSER_NETSCAPE_NAVIGATOR );
810
+ return true;
811
+ } else if ( stripos( $this->_agent, 'Firefox' ) === false && preg_match( '/Netscape6?\/([^ ]*)/i', $this->_agent, $matches ) ) {
812
+ $this->setVersion( $matches[1] );
813
+ $this->setBrowser( $this->BROWSER_NETSCAPE_NAVIGATOR );
814
+ return true;
815
+ }
816
+ return false;
817
+ }
818
 
819
+ /**
820
+ * Determine if the browser is Shiretoko or not (https://wiki.mozilla.org/Projects/shiretoko) (last updated 1.7)
821
+ *
822
+ * @return boolean True if the browser is Shiretoko otherwise false
823
+ */
824
+ function checkBrowserShiretoko() {
825
+ if ( stripos( $this->_agent, 'Mozilla' ) !== false && preg_match( '/Shiretoko\/([^ ]*)/i', $this->_agent, $matches ) ) {
826
+ $this->setVersion( $matches[1] );
827
+ $this->setBrowser( $this->BROWSER_SHIRETOKO );
828
+ return true;
829
+ }
830
+ return false;
831
+ }
832
 
833
+ /**
834
+ * Determine if the browser is Ice Cat or not (http://en.wikipedia.org/wiki/GNU_IceCat) (last updated 1.7)
835
+ *
836
+ * @return boolean True if the browser is Ice Cat otherwise false
837
+ */
838
+ function checkBrowserIceCat() {
839
+ if ( stripos( $this->_agent, 'Mozilla' ) !== false && preg_match( '/IceCat\/([^ ]*)/i', $this->_agent, $matches ) ) {
840
+ $this->setVersion( $matches[1] );
841
+ $this->setBrowser( $this->BROWSER_ICECAT );
842
+ return true;
843
+ }
844
+ return false;
845
+ }
 
 
 
 
 
846
 
847
+ /**
848
+ * Determine if the browser is Nokia or not (last updated 1.7)
849
+ *
850
+ * @return boolean True if the browser is Nokia otherwise false
851
+ */
852
+ function checkBrowserNokia() {
853
+ if ( preg_match( "/Nokia([^\/]+)\/([^ SP]+)/i", $this->_agent, $matches ) ) {
854
+ $this->setVersion( $matches[2] );
855
+ if ( stripos( $this->_agent, 'Series60' ) !== false || strpos( $this->_agent, 'S60' ) !== false ) {
856
+ $this->setBrowser( $this->BROWSER_NOKIA_S60 );
857
+ } else {
858
+ $this->setBrowser( $this->BROWSER_NOKIA );
 
 
 
 
859
  }
860
+ $this->setMobile( true );
861
+ return true;
862
+ }
863
+ return false;
864
+ }
865
 
866
+ /**
867
+ * Determine if the browser is Firefox or not (last updated 1.7)
868
+ *
869
+ * @return boolean True if the browser is Firefox otherwise false
870
+ */
871
+ function checkBrowserFirefox() {
872
+ if ( stripos( $this->_agent, 'safari' ) === false ) {
873
+ if ( preg_match( "/Firefox[\/ \(]([^ ;\)]+)/i", $this->_agent, $matches ) ) {
874
+ $this->setVersion( $matches[1] );
875
+ $this->setBrowser( $this->BROWSER_FIREFOX );
876
+ return true;
877
+ } else if ( preg_match( "/Firefox$/i", $this->_agent, $matches ) ) {
878
+ $this->setVersion( "" );
879
+ $this->setBrowser( $this->BROWSER_FIREFOX );
880
  return true;
881
  }
 
882
  }
883
+ return false;
884
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
885
 
886
+ /**
887
+ * Determine if the browser is Firefox or not (last updated 1.7)
888
+ *
889
+ * @return boolean True if the browser is Firefox otherwise false
890
+ */
891
+ function checkBrowserIceweasel() {
892
+ if ( stripos( $this->_agent, 'Iceweasel' ) !== false ) {
893
+ $aresult = explode( '/', stristr( $this->_agent, 'Iceweasel' ) );
894
+ $aversion = explode( ' ', $aresult[1] );
895
+ $this->setVersion( $aversion[0] );
896
+ $this->setBrowser( $this->BROWSER_ICEWEASEL );
897
+ return true;
898
+ }
899
+ return false;
900
+ }
901
+ /**
902
+ * Determine if the browser is Mozilla or not (last updated 1.7)
903
+ *
904
+ * @return boolean True if the browser is Mozilla otherwise false
905
+ */
906
+ function checkBrowserMozilla() {
907
+ if ( stripos( $this->_agent, 'mozilla' ) !== false && preg_match( '/rv:[0-9].[0-9][a-b]?/i', $this->_agent ) && stripos( $this->_agent, 'netscape' ) === false ) {
908
+ $aversion = explode( ' ', stristr( $this->_agent, 'rv:' ) );
909
+ preg_match( '/rv:[0-9].[0-9][a-b]?/i', $this->_agent, $aversion );
910
+ $this->setVersion( str_replace( 'rv:', '', $aversion[0] ) );
911
+ $this->setBrowser( $this->BROWSER_MOZILLA );
912
+ return true;
913
+ } else if ( stripos( $this->_agent, 'mozilla' ) !== false && preg_match( '/rv:[0-9]\.[0-9]/i', $this->_agent ) && stripos( $this->_agent, 'netscape' ) === false ) {
914
+ $aversion = explode( '', stristr( $this->_agent, 'rv:' ) );
915
+ $this->setVersion( str_replace( 'rv:', '', $aversion[0] ) );
916
+ $this->setBrowser( $this->BROWSER_MOZILLA );
917
+ return true;
918
+ } else if ( stripos( $this->_agent, 'mozilla' ) !== false && preg_match( '/mozilla\/([^ ]*)/i', $this->_agent, $matches ) && stripos( $this->_agent, 'netscape' ) === false ) {
919
+ $this->setVersion( $matches[1] );
920
+ $this->setBrowser( $this->BROWSER_MOZILLA );
921
+ return true;
922
+ }
923
+ return false;
924
+ }
925
 
926
+ /**
927
+ * Determine if the browser is Lynx or not (last updated 1.7)
928
+ *
929
+ * @return boolean True if the browser is Lynx otherwise false
930
+ */
931
+ function checkBrowserLynx() {
932
+ if ( stripos( $this->_agent, 'lynx' ) !== false ) {
933
+ $aresult = explode( '/', stristr( $this->_agent, 'Lynx' ) );
934
+ $aversion = explode( ' ', ( isset( $aresult[1] )?$aresult[1]:"" ) );
935
+ $this->setVersion( $aversion[0] );
936
+ $this->setBrowser( $this->BROWSER_LYNX );
937
+ return true;
938
+ }
939
+ return false;
940
+ }
941
 
942
+ /**
943
+ * Determine if the browser is Amaya or not (last updated 1.7)
944
+ *
945
+ * @return boolean True if the browser is Amaya otherwise false
946
+ */
947
+ function checkBrowserAmaya() {
948
+ if ( stripos( $this->_agent, 'amaya' ) !== false ) {
949
+ $aresult = explode( '/', stristr( $this->_agent, 'Amaya' ) );
950
+ $aversion = explode( ' ', $aresult[1] );
951
+ $this->setVersion( $aversion[0] );
952
+ $this->setBrowser( $this->BROWSER_AMAYA );
953
+ return true;
954
+ }
955
+ return false;
956
+ }
 
 
 
 
957
 
958
+ /**
959
+ * Determine if the browser is Safari or not (last updated 1.7)
960
+ *
961
+ * @return boolean True if the browser is Safari otherwise false
962
+ */
963
+ function checkBrowserSafari() {
964
+ if ( stripos( $this->_agent, 'Safari' ) !== false && stripos( $this->_agent, 'iPhone' ) === false && stripos( $this->_agent, 'iPod' ) === false ) {
965
+ $aresult = explode( '/', stristr( $this->_agent, 'Version' ) );
966
+ if ( isset( $aresult[1] ) ) {
967
+ $aversion = explode( ' ', $aresult[1] );
968
+ $this->setVersion( $aversion[0] );
969
+ } else {
970
+ $this->setVersion( $this->VERSION_UNKNOWN );
971
+ }
972
+ $this->setBrowser( $this->BROWSER_SAFARI );
973
+ return true;
974
+ }
975
+ return false;
976
+ }
 
977
 
978
+ /**
979
+ * Determine if the browser is iPhone or not (last updated 1.7)
980
+ *
981
+ * @return boolean True if the browser is iPhone otherwise false
982
+ */
983
+ function checkBrowseriPhone() {
984
+ if ( stripos( $this->_agent, 'iPhone' ) !== false ) {
985
+ $aresult = explode( '/', stristr( $this->_agent, 'Version' ) );
986
+ if ( isset( $aresult[1] ) ) {
987
+ $aversion = explode( ' ', $aresult[1] );
988
+ $this->setVersion( $aversion[0] );
989
+ } else {
990
+ $this->setVersion( $this->VERSION_UNKNOWN );
991
+ }
992
+ $this->setMobile( true );
993
+ $this->setBrowser( $this->BROWSER_IPHONE );
994
+ return true;
995
+ }
996
+ return false;
997
+ }
998
 
999
+ /**
1000
+ * Determine if the browser is iPod or not (last updated 1.7)
1001
+ *
1002
+ * @return boolean True if the browser is iPod otherwise false
1003
+ */
1004
+ function checkBrowseriPad() {
1005
+ if ( stripos( $this->_agent, 'iPad' ) !== false ) {
1006
+ $aresult = explode( '/', stristr( $this->_agent, 'Version' ) );
1007
+ if ( isset( $aresult[1] ) ) {
1008
+ $aversion = explode( ' ', $aresult[1] );
1009
+ $this->setVersion( $aversion[0] );
1010
+ } else {
1011
+ $this->setVersion( $this->VERSION_UNKNOWN );
1012
+ }
1013
+ $this->setMobile( true );
1014
+ $this->setBrowser( $this->BROWSER_IPAD );
1015
+ return true;
1016
+ }
1017
+ return false;
1018
+ }
1019
 
1020
+ /**
1021
+ * Determine if the browser is iPod or not (last updated 1.7)
1022
+ *
1023
+ * @return boolean True if the browser is iPod otherwise false
1024
+ */
1025
+ function checkBrowseriPod() {
1026
+ if ( stripos( $this->_agent, 'iPod' ) !== false ) {
1027
+ $aresult = explode( '/', stristr( $this->_agent, 'Version' ) );
1028
+ if ( isset( $aresult[1] ) ) {
1029
+ $aversion = explode( ' ', $aresult[1] );
1030
+ $this->setVersion( $aversion[0] );
1031
+ } else {
1032
+ $this->setVersion( $this->VERSION_UNKNOWN );
1033
+ }
1034
+ $this->setMobile( true );
1035
+ $this->setBrowser( $this->BROWSER_IPOD );
1036
+ return true;
1037
+ }
1038
+ return false;
1039
+ }
1040
 
1041
+ /**
1042
+ * Determine if the browser is Android or not (last updated 1.7)
1043
+ *
1044
+ * @return boolean True if the browser is Android otherwise false
1045
+ */
1046
+ function checkBrowserAndroid() {
1047
+ if ( stripos( $this->_agent, 'Android' ) !== false ) {
1048
+ $aresult = explode( ' ', stristr( $this->_agent, 'Android' ) );
1049
+ if ( isset( $aresult[1] ) ) {
1050
+ $aversion = explode( ' ', $aresult[1] );
1051
+ $this->setVersion( $aversion[0] );
1052
+ } else {
1053
+ $this->setVersion( $this->VERSION_UNKNOWN );
1054
+ }
1055
+ $this->setMobile( true );
1056
+ $this->setBrowser( $this->BROWSER_ANDROID );
1057
+ return true;
1058
+ }
1059
+ return false;
1060
+ }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1061
 
1062
+ /**
1063
+ * Determine the user's platform (last updated 1.7)
1064
+ */
1065
+ function checkPlatform() {
1066
+ if ( stripos( $this->_agent, 'windows' ) !== false ) {
1067
+ $this->_platform = $this->PLATFORM_WINDOWS;
1068
+ } else if ( stripos( $this->_agent, 'iPad' ) !== false ) {
1069
+ $this->_platform = $this->PLATFORM_IPAD;
1070
+ } else if ( stripos( $this->_agent, 'iPod' ) !== false ) {
1071
+ $this->_platform = $this->PLATFORM_IPOD;
1072
+ } else if ( stripos( $this->_agent, 'iPhone' ) !== false ) {
1073
+ $this->_platform = $this->PLATFORM_IPHONE;
1074
+ } elseif ( stripos( $this->_agent, 'mac' ) !== false ) {
1075
+ $this->_platform = $this->PLATFORM_APPLE;
1076
+ } elseif ( stripos( $this->_agent, 'android' ) !== false ) {
1077
+ $this->_platform = $this->PLATFORM_ANDROID;
1078
+ } elseif ( stripos( $this->_agent, 'linux' ) !== false ) {
1079
+ $this->_platform = $this->PLATFORM_LINUX;
1080
+ } else if ( stripos( $this->_agent, 'Nokia' ) !== false ) {
1081
+ $this->_platform = $this->PLATFORM_NOKIA;
1082
+ } else if ( stripos( $this->_agent, 'BlackBerry' ) !== false ) {
1083
+ $this->_platform = $this->PLATFORM_BLACKBERRY;
1084
+ } elseif ( stripos( $this->_agent, 'FreeBSD' ) !== false ) {
1085
+ $this->_platform = $this->PLATFORM_FREEBSD;
1086
+ } elseif ( stripos( $this->_agent, 'OpenBSD' ) !== false ) {
1087
+ $this->_platform = $this->PLATFORM_OPENBSD;
1088
+ } elseif ( stripos( $this->_agent, 'NetBSD' ) !== false ) {
1089
+ $this->_platform = $this->PLATFORM_NETBSD;
1090
+ } elseif ( stripos( $this->_agent, 'OpenSolaris' ) !== false ) {
1091
+ $this->_platform = $this->PLATFORM_OPENSOLARIS;
1092
+ } elseif ( stripos( $this->_agent, 'SunOS' ) !== false ) {
1093
+ $this->_platform = $this->PLATFORM_SUNOS;
1094
+ } elseif ( stripos( $this->_agent, 'OS\/2' ) !== false ) {
1095
+ $this->_platform = $this->PLATFORM_OS2;
1096
+ } elseif ( stripos( $this->_agent, 'BeOS' ) !== false ) {
1097
+ $this->_platform = $this->PLATFORM_BEOS;
1098
+ } elseif ( stripos( $this->_agent, 'win' ) !== false ) {
1099
+ $this->_platform = $this->PLATFORM_WINDOWS;
1100
+ }
1101
 
1102
+ }
1103
+ }
includes/licensing.php CHANGED
@@ -1,783 +1,86 @@
1
  <?php
2
 
3
  /**
4
- * What to print in place of license code chars.
5
- * This must not be a symbol that is considered to be a valid license key char.
6
  *
7
- * @since 4.6.10
 
8
  */
9
- define( 'WPRSS_LICENSE_KEY_MASK_CHAR', '•' );
10
- /**
11
- * How many characters of the license code to print as is.
12
- * Use negative value to indicate that characters at the end of the key are excluded.
13
- *
14
- * @since 4.6.10
15
- */
16
- define( 'WPRSS_LICENSE_KEY_MASK_EXCLUDE_AMOUNT', -4 );
17
-
18
- /**
19
- * Returns all registered addons.
20
- *
21
- * @since 4.4.5
22
- */
23
- function wprss_get_addons() {
24
- return apply_filters( 'wprss_register_addon', array() );
25
- }
26
-
27
-
28
- /**
29
- * Calls the EDD Software Licensing API to perform licensing tasks on the addon's store server.
30
- *
31
- * @since 4.4.5
32
- */
33
- function wprss_edd_licensing_api( $addon, $license_key = NULL, $action = 'check_license', $return = 'license' ) {
34
- // If no license argument was given
35
- if ( $license_key === NULL ) {
36
- // Get the license key
37
- $license_key = wprss_get_license_key( $addon );
38
- }
39
- // Get the license status from the DB
40
- $license_status = wprss_get_license_status( $addon );
41
-
42
- // Prepare constants
43
- $item_name = strtoupper( $addon );
44
- $item_name_constant = constant( "WPRSS_{$item_name}_SL_ITEM_NAME" );
45
- $store_url_constant = constant( "WPRSS_{$item_name}_SL_STORE_URL" );
46
-
47
- // data to send in our API request
48
- $api_params = array(
49
- 'edd_action' => $action,
50
- 'license' => sanitize_text_field( $license_key ),
51
- 'item_name' => urlencode( $item_name_constant ),
52
- 'url' => urlencode( network_site_url() ),
53
- 'time' => time(),
54
- );
55
-
56
- // Send the request to the API
57
- $response = wp_remote_get( add_query_arg( $api_params, $store_url_constant ) );
58
-
59
- // If the response is an error, return the value in the DB
60
- if ( is_wp_error( $response ) ) {
61
- wprss_log( sprintf( 'Licensing API request failed: %1$s', $response->get_error_message() ), __FUNCTION__, WPRSS_LOG_LEVEL_WARNING );
62
- return $license_status;
63
- }
64
-
65
- // decode the license data
66
- $license_data = json_decode( wp_remote_retrieve_body( $response ) );
67
-
68
- // Could not decode response JSON
69
- if ( is_null( $license_data ) ) {
70
- wprss_log( sprintf( 'Licensing API: Failed to decode response JSON' ), __FUNCTION__, WPRSS_LOG_LEVEL_WARNING );
71
- return $license_status;
72
- }
73
-
74
- // Update the DB option
75
- $license_statuses = get_option( 'wprss_settings_license_statuses' );
76
- $license_statuses["{$addon}_license_status"] = $license_data->license;
77
- $license_statuses["{$addon}_license_expires"] = $license_data->expires;
78
- update_option( 'wprss_settings_license_statuses', $license_statuses );
79
-
80
- // Return the data
81
- if ( strtoupper( $return ) === 'ALL' ) {
82
- return $license_data;
83
- } else {
84
- return isset( $license_data->{$return} ) ? $license_data->{$return} : null;
85
- }
86
- }
87
-
88
-
89
- /**
90
- * Returns the license status. Also updates the status in the DB.
91
- *
92
- * @since 4.4.5
93
- */
94
- function wprss_edd_check_license( $addon, $license_key = NULL, $return = 'license' ) {
95
- return wprss_edd_licensing_api( $addon, $license_key, 'check_license', $return );
96
- }
97
-
98
-
99
- /**
100
- * Activates an addon's license.
101
- *
102
- * @since 4.4.5
103
- */
104
- function wprss_edd_activate_license( $addon, $license_key = NULL ) {
105
- return wprss_edd_licensing_api( $addon, $license_key, 'activate_license' );
106
- }
107
-
108
-
109
- /**
110
- * Deactivates an addon's license.
111
- *
112
- * @since 4.4.5
113
- */
114
- function wprss_edd_deactivate_license( $addon, $license_key = NULL ) {
115
- return wprss_edd_licensing_api( $addon, $license_key, 'deactivate_license' );
116
- }
117
-
118
-
119
- /**
120
- * Returns an array of the default license settings. Used for plugin activation.
121
- *
122
- * @since 4.4.5
123
- *
124
- */
125
- function wprss_default_license_settings( $addon ) {
126
- // Set up the default license settings
127
- $settings = apply_filters(
128
- 'wprss_default_license_settings',
129
- array(
130
- "{$addon}_license_key" => FALSE,
131
- "{$addon}_license_status" => 'invalid',
132
- "{$addon}_license_expires" => NULL
133
- )
134
- );
135
-
136
- // Return the default settings
137
- return $settings;
138
- }
139
-
140
-
141
- /**
142
- * Returns the saved license code.
143
- *
144
- * @since 4.4.5
145
- */
146
- function wprss_get_license_key( $addon ) {
147
- // Get default and current options
148
- $defaults = wprss_default_license_settings( $addon );
149
- $keys = get_option( 'wprss_settings_license_keys', array() );
150
- // Prepare the array key and target
151
- $k = "{$addon}_license_key";
152
- // Return the appropriate value
153
- return isset( $keys["{$addon}_license_key"] )? $keys[$k] : $defaults[$k];
154
- }
155
-
156
-
157
- /**
158
- * Returns the saved license code.
159
- *
160
- * @since 4.4.5
161
- */
162
- function wprss_get_license_status( $addon ) {
163
- // Get the default and current options
164
- $defaults = wprss_default_license_settings( $addon );
165
- $statuses = get_option( 'wprss_settings_license_statuses', array() );
166
- // Prepare the key
167
- $k = "{$addon}_license_status";
168
- // Return the appropriate value
169
- return isset( $statuses["{$addon}_license_status"] )? $statuses[$k] : $defaults[$k];
170
- }
171
-
172
-
173
- /**
174
- * Returns the saved license expiry.
175
- *
176
- * @since 4.6.7
177
- */
178
- function wprss_get_license_expiry( $addon ) {
179
- // Get default and current options
180
- $defaults = wprss_default_license_settings( $addon );
181
- $statuses = get_option( 'wprss_settings_license_statuses', array() );
182
- // Prepare the key
183
- $k = "{$addon}_license_expires";
184
- // Return the appropriate value
185
- return isset( $statuses[$k] ) ? $statuses[$k] : $defaults[$k];
186
- }
187
-
188
-
189
- add_action( 'admin_init', 'wprss_check_to_show_license_notice');
190
- /**
191
- * Checks whether there are any invalid or expired licenses.
192
- *
193
- * @since 4.6.10
194
- * @return BOOL which is TRUE if any addons are unlicensed, FALSE otherwise.
195
- */
196
- function wprss_unlicensed_addons_exist() {
197
- // Get the license statuses including expiry dates.
198
- $statuses = get_option( 'wprss_settings_license_statuses', array() );
199
-
200
- foreach ($statuses as $key => $value) {
201
- if ( strpos($key, '_license_status') > 0 ) {
202
- if ( $value !== 'valid') {
203
- return TRUE;
204
- }
205
- } else if ( strpos($key, '_license_expires') > 0 ) {
206
- // Check invalid expiry dates.
207
- $expires = strtotime( substr( $value, 0, strpos( $value, " " ) ) );
208
-
209
- if ( $expires == 0 || ( $expires < strtotime("+2 weeks") ) ) {
210
- return TRUE;
211
- }
212
- }
213
- }
214
-
215
- return FALSE;
216
- }
217
-
218
- /**
219
- * Check if any add-ons have a valid license and return a boolean.
220
- *
221
- * @since 4.6.8
222
- */
223
- function wprss_is_premium_user() {
224
- // Iterate through license statuses looking for a valid one.
225
- $statuses = get_option('wprss_settings_license_statuses', array());
226
- foreach ($statuses as $key => $value) {
227
- // If we're looking at a license status key...
228
- if (strpos($key, '_license_status') !== FALSE) {
229
- // ...and the license is valid...
230
- if ($value === 'valid') {
231
- return TRUE;
232
- }
233
- }
234
- }
235
-
236
- return FALSE;
237
- }
238
-
239
- /**
240
- * Returns an array of addon IDs that are licensed.
241
- *
242
- * @since 4.6.10
243
- * @return Array of addon ID strings that have a valid license status.
244
- */
245
- function wprss_get_licensed_addons() {
246
- $addons = array();
247
-
248
- // Get the license statuses.
249
- $statuses = get_option( 'wprss_settings_license_statuses', array() );
250
-
251
- foreach ($statuses as $key => $value) {
252
- if ( strpos($key, '_license_status') > 0 ) {
253
- if ( $value === 'valid') {
254
- $addons[] = strtoupper( substr( $key, 0, strpos( $key, "_" ) ) );
255
- }
256
- }
257
- }
258
-
259
- return $addons;
260
-
261
- }
262
-
263
-
264
- /**
265
- * Checks whether we should show the invalid/expired license notices.
266
- *
267
- * @since 4.6.10
268
- */
269
- function wprss_check_to_show_license_notice() {
270
- // Check if we found any of the licenses to be invalid, expiring or expired
271
- // so that we can show the appropriate license nag.
272
- if (wprss_unlicensed_addons_exist()) {
273
- add_action( 'all_admin_notices', 'wprss_show_license_notice' );
274
- }
275
- }
276
-
277
-
278
- /**
279
- * Shows an admin notice for any invalid/expired licenses.
280
- *
281
- * @since 4.6.9
282
- */
283
- function wprss_show_license_notice() {
284
- // Get the license statuses including expiry dates.
285
- $statuses = get_option( 'wprss_settings_license_statuses', array() );
286
-
287
- // Array of notices to show.
288
- $notices = array();
289
-
290
- foreach ($statuses as $key => $value) {
291
- if ( strpos($key, '_license_status') > 0 ) {
292
- if ( $value === 'expired' ) {
293
- // License is expired, but we'll show the notice for this when checking the *_license_expires key.
294
-
295
- continue;
296
-
297
- } else if ( $value !== 'valid' ) {
298
- // The license is invalid or unactivated.
299
-
300
- $uid = strtoupper( substr( $key, 0, strpos( $key, "_" ) ) );
301
-
302
- // Check if the plugin is currently activated.
303
- if ( !defined("WPRSS_{$uid}_SL_ITEM_NAME") ) {
304
- continue;
305
- } else {
306
- $plugin = constant("WPRSS_{$uid}_SL_ITEM_NAME");
307
- }
308
-
309
- $msg = sprintf(
310
- __( 'Remember to <a href="%s">enter your plugin license code</a> for the WP RSS Aggregator <b>%s</b> add-on to benefit from updates and support.', WPRSS_TEXT_DOMAIN ),
311
- esc_attr(admin_url( 'edit.php?post_type=wprss_feed&page=wprss-aggregator-settings&tab=licenses_settings' )),
312
- $plugin
313
- );
314
-
315
- // Save the notice we're going to display
316
- $notices[$uid] = '<div id="wprss-license-notice-' . $uid . '" class="error wprss-license-notice"><p>' . $msg . '</p></div>';
317
- }
318
- } else if ( strpos($key, '_license_expires') > 0 ) {
319
- // Check for expired licenses
320
-
321
- $expires = strtotime( substr( $value, 0, strpos( $value, " " ) ) );
322
- $id = substr( $key, 0, strpos( $key, "_" ) );
323
- $uid = strtoupper($id);
324
-
325
- $addon_notices = get_option('wprss_addon_notices');
326
-
327
- // Check if the plugin is currently activated.
328
- if ( !defined("WPRSS_{$uid}_SL_ITEM_NAME") ) {
329
- continue;
330
- } else {
331
- $plugin = constant("WPRSS_{$uid}_SL_ITEM_NAME");
332
- }
333
-
334
- if ( $expires < strtotime("+2 weeks") && empty ( $addon_notices[$id]['expiry'] ) ) {
335
- // The license is expired or expiring soon.
336
- $license_key = wprss_get_license_key($id);
337
- $msg = sprintf(
338
- __('<a href="%s">Save 30%% on your license renewal</a> for the WP RSS Aggregator <b>%s</b> add-on and continue receiving updates and support.', WPRSS_TEXT_DOMAIN),
339
- esc_attr(WPRSS_SL_STORE_URL . '/checkout/?edd_license_key=' . $license_key),
340
- $plugin
341
- );
342
-
343
- // User can hide expiring/expired license messages.
344
- $hide = '<a href="#" class="ajax-close-addon-notice" style="float:right;" data-addon="'. $id .'" data-notice="expiry">' .
345
- __('Dismiss this notification', WPRSS_TEXT_DOMAIN) . '</a>';
346
-
347
- // Only show this notice if there isn't already a notice to show for this add-on.
348
- if ( !isset($notices[$uid]) ) {
349
- $notices[$uid] = '<div class="error wprss-license-notice"><p>' . $msg . $hide . '</p></div>';
350
- }
351
- }
352
- }
353
- }
354
-
355
- // Display the notices
356
- foreach ($notices as $notice) {
357
- echo $notice;
358
- }
359
  }
360
 
361
 
362
- add_action( 'wp_ajax_wprss_ajax_manage_license', 'wprss_ajax_manage_license' );
363
  /**
364
- * Handles the AJAX request to check a license.
365
  *
366
- * @since 4.7
 
367
  */
368
- function wprss_ajax_manage_license() {
369
- // Get and sanitize the addon ID we're checking.
370
- if ( isset($_GET['addon']) ) {
371
- $addon = sanitize_text_field($_GET['addon']);
372
- } else {
373
- wprss_echo_error_and_die( __('No addon ID', WPRSS_TEXT_DOMAIN ));
374
- }
375
-
376
- // Check what we've been asked to do with the license.
377
- if ( isset($_GET['event']) ) {
378
- $event = sanitize_text_field($_GET['event']);
379
-
380
- if ($event !== 'activate' && $event !== 'deactivate') {
381
- wprss_echo_error_and_die( __('Invalid event specified', WPRSS_TEXT_DOMAIN), $addon);
382
- }
383
- } else {
384
- wprss_echo_error_and_die( __('No event specified', WPRSS_TEXT_DOMAIN), $addon);
385
- }
386
-
387
- // Get and sanitize the license that was entered.
388
- if ( isset($_GET['license']) ) {
389
- $license = sanitize_text_field($_GET['license']);
390
- } else {
391
- wprss_echo_error_and_die( __('No license', WPRSS_TEXT_DOMAIN), $addon);
392
- }
393
-
394
- // Check the nonce for this particular add-on's validation button.
395
- if ( isset($_GET['nonce']) ) {
396
- $nonce = sanitize_text_field($_GET['nonce']);
397
- $nonce_id = "wprss_{$addon}_license_nonce";
398
-
399
- if ( !wp_verify_nonce($nonce, $nonce_id) ) {
400
- wprss_echo_error_and_die( __('Bad nonce', WPRSS_TEXT_DOMAIN), $addon);
401
- }
402
- } else {
403
- wprss_echo_error_and_die( __('No nonce', WPRSS_TEXT_DOMAIN), $addon);
404
- }
405
-
406
- $license_keys = get_option('wprss_settings_license_keys', array());
407
- // Check if the license key was obfuscated on the client's end.
408
- if ( wprss_license_key_is_obfuscated( $license ) ) {
409
- // If so, use the stored license key for de/activation.
410
- $license = $license_keys[$addon . '_license_key'];
411
- } else {
412
- // Otherwise, update the license key stored in the DB.
413
- $license_keys[$addon . '_license_key'] = $license;
414
- update_option('wprss_settings_license_keys', $license_keys);
415
- }
416
 
417
- // Call the appropriate EDD licensing function.
418
- if ($event === 'activate') {
419
- $status = wprss_edd_activate_license($addon, $license);
420
- } else if ($event === 'deactivate') {
421
- $status = wprss_edd_deactivate_license($addon, $license);
422
- } else {
423
- wprss_echo_error_and_die( __('Invalid event specified', WPRSS_TEXT_DOMAIN), $addon);
424
- }
425
 
426
- // Assemble the JSON data to return.
427
- $ret = array();
428
-
429
- // Set the validity of the license.
430
- if ( $status === 'site_inactive' ) $status = 'inactive';
431
- if ( $status === 'item_name_mismatch' ) $status = 'invalid';
432
- $ret['validity'] = $status;
433
-
434
- // Set the addon ID for use in the callback.
435
- $ret['addon'] = $addon;
436
-
437
- // Set the HTML markup for the new button and validity display.
438
- $ret['html'] = wprss_get_activate_license_button($addon);
439
-
440
- $ret['licensedAddons'] = wprss_get_licensed_addons();
441
-
442
- // Return the JSON data.
443
- echo json_encode($ret);
444
- die();
445
  }
446
 
447
-
448
- add_action( 'wp_ajax_wprss_ajax_fetch_license', 'wprss_ajax_fetch_license' );
449
  /**
450
- * Handles the AJAX request to fetch a license's information.
451
  *
452
- * @since 4.7
 
453
  */
454
- function wprss_ajax_fetch_license() {
455
- // Get and sanitize the addon ID we're checking.
456
- if ( isset($_GET['addon']) ) {
457
- $addon = sanitize_text_field($_GET['addon']);
458
- } else {
459
- wprss_echo_error_and_die( __('No addon ID', WPRSS_TEXT_DOMAIN ));
460
- }
461
-
462
- // Get the license information from EDD
463
- $ret = wprss_edd_check_license( $addon, NULL, 'ALL' );
464
-
465
- echo json_encode($ret);
466
- die();
467
  }
468
 
469
-
470
  /**
471
- * Helper function that echoes a JSON error along with the new
472
- * activate/deactivate license button HTML markup and then die()s.
473
- *
474
- * @since 4.7
475
- */
476
- function wprss_echo_error_and_die($msg, $addon = '') {
477
- $ret = array(
478
- 'error' => $msg,
479
- 'html' => wprss_get_activate_license_button($addon)
480
- );
481
-
482
- echo json_encode($ret);
483
- die();
484
- }
485
-
486
-
487
- add_action( 'wprss_admin_init', 'wprss_license_settings', 100 );
488
- /**
489
- * Adds the license sections and settings for registered add-ons.
490
- *
491
- * @since 4.4.5
492
- */
493
- function wprss_license_settings() {
494
- $addons = wprss_get_addons();
495
- foreach( $addons as $addon_id => $addon_name ) {
496
- // Settings Section
497
- add_settings_section(
498
- "wprss_settings_{$addon_id}_licenses_section",
499
- $addon_name .' '. __( 'License', WPRSS_TEXT_DOMAIN ),
500
- '__return_empty_string',
501
- 'wprss_settings_license_keys'
502
- );
503
- // License key field
504
- add_settings_field(
505
- "wprss_settings_{$addon_id}_license",
506
- __( 'License Key', WPRSS_TEXT_DOMAIN ),
507
- 'wprss_license_key_field',
508
- 'wprss_settings_license_keys',
509
- "wprss_settings_{$addon_id}_licenses_section",
510
- array( $addon_id )
511
- );
512
- // Activate license button
513
- add_settings_field(
514
- "wprss_settings_{$addon_id}_activate_license",
515
- __( 'Activate License', WPRSS_TEXT_DOMAIN ),
516
- 'wprss_activate_license_button',
517
- 'wprss_settings_license_keys',
518
- "wprss_settings_{$addon_id}_licenses_section",
519
- array( $addon_id )
520
- );
521
- }
522
- }
523
-
524
-
525
- /**
526
- * Renders the license field for a particular add-on.
527
- *
528
- * @since 4.4.5
529
- */
530
- function wprss_license_key_field( $args ) {
531
- $addon_id = $args[0];
532
- $license_key = wprss_get_license_key( $addon_id );
533
- $license_key_length = strlen( $license_key );
534
- $mask_char = WPRSS_LICENSE_KEY_MASK_CHAR;
535
- // How many chars to show
536
- $mask_exclude_amount = WPRSS_LICENSE_KEY_MASK_EXCLUDE_AMOUNT;
537
- $mask_exclude_amount = abs( $mask_exclude_amount ) > ($license_key_length - 1)
538
- ? ($license_key_length - 1) * ( $mask_exclude_amount < 0 ? -1 : 1 ) // Making sure to preserve position of mask
539
- : $mask_exclude_amount;
540
- // How many chars to mask. Always at least one char will be masked.
541
- $mask_length = $license_key_length - abs( $mask_exclude_amount );
542
- $mask = $mask_length > 0 ? str_repeat( $mask_char, $mask_length ) : '';
543
- $excluded_chars = WPRSS_MBString::mb_substr( $license_key, $mask_exclude_amount < 0 ? $mask_length : 0, abs( $mask_exclude_amount ) );
544
- $displayed_key = sprintf( $mask_exclude_amount > 0 ? '%1$s%2$s' : '%2$s%1$s', $excluded_chars, $mask); ?>
545
- <input id="wprss-<?php echo $addon_id ?>-license-key" name="wprss_settings_license_keys[<?php echo $addon_id ?>_license_key]"
546
- type="text" value="<?php echo esc_attr( $displayed_key ) ?>" style="width: 300px;"
547
- />
548
- <label class="description" for="wprss-<?php echo $addon_id ?>-license-key">
549
- <?php _e( 'Enter your license key', WPRSS_TEXT_DOMAIN ) ?>
550
- </label><?php
551
- }
552
-
553
-
554
- /**
555
- * Renders the activate/deactivate license button for a particular add-on.
556
  *
557
  * @since 4.4.5
 
 
558
  */
559
- function wprss_activate_license_button( $args ) {
560
- $addon_id = $args[0];
561
- $data = wprss_edd_check_license( $addon_id, NULL, 'ALL' );
562
- $status = is_string( $data ) ? $data : $data->license;
563
- if ( $status === 'site_inactive' ) $status = 'inactive';
564
- if ( $status === 'item_name_mismatch' ) $status = 'invalid';
565
-
566
- $valid = $status == 'valid';
567
- $btn_text = $valid ? 'Deactivate License' : 'Activate License';
568
- $btn_name = "wprss_{$addon_id}_license_" . ( $valid? 'deactivate' : 'activate' );
569
- $btn_class = "button-" . ( $valid ? 'deactivate' : 'activate' ) . "-license";
570
- wp_nonce_field( "wprss_{$addon_id}_license_nonce", "wprss_{$addon_id}_license_nonce", false ); ?>
571
-
572
- <input type="button" class="<?php echo $btn_class; ?> button-process-license button-secondary" name="<?php echo $btn_name; ?>" value="<?php _e( $btn_text, WPRSS_TEXT_DOMAIN ); ?>" />
573
- <span id="wprss-<?php echo $addon_id; ?>-license-status-text">
574
- <strong><?php _e('Status', WPRSS_TEXT_DOMAIN); ?>:
575
- <span class="wprss-<?php echo $addon_id; ?>-license-<?php echo $status; ?>">
576
- <?php _e( ucfirst($status), WPRSS_TEXT_DOMAIN ); ?>
577
- <?php if ( $status === 'valid' ) : ?>
578
- <i class="fa fa-check"></i>
579
- <?php elseif( $status === 'invalid' || $status === 'expired' ): ?>
580
- <i class="fa fa-times"></i>
581
- <?php elseif( $status === 'inactive' ): ?>
582
- <i class="fa fa-warning"></i>
583
- <?php endif; ?>
584
- </strong>
585
- </span>
586
- </span>
587
-
588
- <p>
589
- <?php
590
- $license_key = wprss_get_license_key( $addon_id );
591
- if ( ! empty( $license_key ) ) :
592
- if ( is_object( $data ) ) :
593
- $acts_current = $data->site_count;
594
- $acts_left = $data->activations_left;
595
- $acts_limit = $data->license_limit;
596
- $expires = $data->expires;
597
- $expires = substr( $expires, 0, strpos( $expires, " " ) );
598
-
599
- // If the license key is garbage, don't show any of the data.
600
- if ( !empty($data->payment_id) && !empty($data->license_limit ) ) :
601
- ?>
602
- <small>
603
- <?php if ( $status !== 'valid' && $acts_left === 0 ) : ?>
604
- <?php $account_url = 'https://www.wprssaggregator.com/account/?action=manage_licenses&payment_id=' . $data->payment_id; ?>
605
- <a href="<?php echo $account_url; ?>"><?php _e("No activations left. Click here to manage the sites you've activated licenses on.", WPRSS_TEXT_DOMAIN); ?></a>
606
- <br/>
607
- <?php endif; ?>
608
- <?php if ( strtotime($expires) < strtotime("+2 weeks") ) : ?>
609
- <?php $renewal_url = esc_attr(WPRSS_SL_STORE_URL . '/checkout/?edd_license_key=' . $license_key); ?>
610
- <a href="<?php echo $renewal_url; ?>"><?php _e('Renew your license to continue receiving updates and support.', WPRSS_TEXT_DOMAIN); ?></a>
611
- <br/>
612
- <?php endif; ?>
613
- <strong><?php _e('Activations', WPRSS_TEXT_DOMAIN); ?>:</strong>
614
- <?php echo $acts_current.'/'.$acts_limit; ?> (<?php echo $acts_left; ?> left)
615
- <br/>
616
- <strong><?php _e('Expires on', WPRSS_TEXT_DOMAIN); ?>:</strong>
617
- <code><?php echo $expires; ?></code>
618
- <br/>
619
- <strong><?php _e('Registered to', WPRSS_TEXT_DOMAIN); ?>:</strong>
620
- <?php echo $data->customer_name; ?> (<code><?php echo $data->customer_email; ?></code>)
621
- </small>
622
- <?php endif; ?>
623
- <?php else: ?>
624
- <small><?php _e('Failed to get license information. This is a temporary problem. Check your internet connection and try again later.', WPRSS_TEXT_DOMAIN); ?></small>
625
- <?php endif; ?>
626
- <?php endif;
627
- ?>
628
- </p>
629
-
630
- <style type="text/css">
631
- .wprss-<?php echo $addon_id; ?>-license-valid {
632
- color: green;
633
- }
634
- .wprss-<?php echo $addon_id; ?>-license-invalid, .wprss-<?php echo $addon_id; ?>-license-expired {
635
- color: #b71919;
636
- }
637
- .wprss-<?php echo $addon_id; ?>-license-inactive {
638
- color: #d19e5b;
639
- }
640
- #wprss-<?php echo $addon_id; ?>-license-status-text {
641
- margin-left: 8px;
642
- line-height: 27px;
643
- vertical-align: middle;
644
- }
645
- </style>
646
-
647
-
648
- <?php
649
- }
650
-
651
-
652
- /**
653
- * Returns the activate/deactivate license button markup for a particular add-on.
654
- *
655
- * @since 4.7
656
- */
657
- function wprss_get_activate_license_button( $addon ) {
658
- // Buffer the output from the rendering function.
659
- ob_start();
660
-
661
- wprss_activate_license_button(array($addon));
662
- $ret = ob_get_contents();
663
-
664
- ob_end_clean();
665
-
666
- return $ret;
667
- }
668
-
669
-
670
- add_action( 'admin_init', 'wprss_process_addon_license', 10 );
671
- /**
672
- * Handles the activation/deactivation process
673
- *
674
- * @since 1.0
675
- */
676
- function wprss_process_addon_license() {
677
- $addons = wprss_get_addons();
678
-
679
- // Get for each registered addon
680
- foreach( $addons as $id => $name ) {
681
-
682
- // listen for our activate button to be clicked
683
- if( isset( $_POST["wprss_{$id}_license_activate"] ) || isset( $_POST["wprss_{$id}_license_deactivate"] ) ) {
684
- // run a quick security check
685
- if( ! check_admin_referer( "wprss_{$id}_license_nonce", "wprss_{$id}_license_nonce" ) )
686
- continue; // get out if we didn't click the Activate/Deactivate button
687
- }
688
-
689
- // retrieve the license keys and statuses from the database
690
- $license = wprss_get_license_key( $id );
691
- $license_statuses = get_option( 'wprss_settings_license_statuses' );
692
-
693
- // If the license is not saved in DB, but is included in POST
694
- if ( $license == '' && !empty($_POST['wprss_settings_license_keys'][$id.'_license_key']) ) {
695
- // Use the license given in POST
696
- $license = $_POST['wprss_settings_license_keys'][$id.'_license_key'];
697
- }
698
-
699
- // Prepare the action to take
700
- if ( isset( $_POST["wprss_{$id}_license_activate"] ) ) {
701
- wprss_edd_activate_license( $id, $license );
702
- }
703
- elseif ( isset( $_POST["wprss_{$id}_license_deactivate"] ) ) {
704
- wprss_edd_deactivate_license( $id, $license );
705
- }
706
- }
707
  }
708
 
709
-
710
- add_action( 'admin_init', 'wprss_setup_edd_updater' );
711
  /**
712
- * Sets up the EDD updater for all registered add-ons.
713
- *
714
- * @since 4.6.3
715
  */
716
- function wprss_setup_edd_updater() {
717
- if ( ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) || ( defined( 'DOING_AJAX' ) && DOING_AJAX ) ) return;
 
 
 
718
 
719
- // Get all registered addons
720
- $addons = wprss_get_addons();
 
721
 
722
- // retrieve our license key from the DB
723
- $licenses = get_option( 'wprss_settings_license_keys' );
724
-
725
- // setup the updater
726
- if ( !class_exists( 'EDD_SL_Plugin_Updater' ) ) {
727
- // load our custom updater
728
- include ( WPRSS_INC . 'libraries/EDD_licensing/EDD_SL_Plugin_Updater.php' );
729
- }
730
-
731
- // Iterate the addons
732
- foreach( $addons as $id => $name ) {
733
- // Prepare the data
734
- $license = wprss_get_license_key( $id );
735
- $uid = strtoupper( $id );
736
- $name = constant("WPRSS_{$uid}_SL_ITEM_NAME");
737
- $version = constant("WPRSS_{$uid}_VERSION");
738
- $path = constant("WPRSS_{$uid}_PATH");
739
- // Set up an updater
740
- $edd_updater = new EDD_SL_Plugin_Updater( WPRSS_SL_STORE_URL, $path, array(
741
- 'version' => $version, // current version number
742
- 'license' => $license, // license key (used get_option above to retrieve from DB)
743
- 'item_name' => $name, // name of this plugin
744
- 'author' => 'Jean Galea' // author of this plugin
745
- ));
746
- }
747
- }
748
 
 
 
 
749
 
750
- add_filter( 'wprss_settings_license_key_is_valid', 'wprss_license_validate_key_for_save', 10, 2 );
751
- /**
752
- * Invalidates the key if it is obfuscated, causing the saved version to be used.
753
- * This meanst that the new key will not be saved, as it is considered then to be unchanged.
754
- *
755
- * @since 4.6.10
756
- * @param bool $is_valid Indicates whether the key is currently considered to be valid.
757
- * @param string $key The license key in question
758
- * @return Whether or not the key is still to be considered valid.
759
- */
760
- function wprss_license_validate_key_for_save( $is_valid, $key ) {
761
- if ( wprss_license_key_is_obfuscated( $key ) )
762
- return false;
763
-
764
- return $is_valid;
765
- }
766
-
767
-
768
- /**
769
- * Determines whether or not the license key in question is obfuscated.
770
- *
771
- * This is achieved by searching for the mask character in the key. Because the
772
- * mask character cannot be a valid license character, the presence of at least
773
- * one such character indicates that the key is obfuscated.
774
- *
775
- * @since 4.6.10
776
- * @param string $key The license key in question.
777
- * @return bool Whether or not this key is obfuscated.
778
- */
779
- function wprss_license_key_is_obfuscated( $key ) {
780
- $char = WPRSS_LICENSE_KEY_MASK_CHAR;
781
-
782
- return WPRSS_MBString::mb_strpos( $key, $char ) !== false;
783
  }
1
  <?php
2
 
3
  /**
4
+ * Gets the singleton instance of the Settings class, creating it if it doesn't exist.
 
5
  *
6
+ * @since 4.7.8
7
+ * @return Aventura\Wprss\Core\Licensing\Settings
8
  */
9
+ function wprss_licensing_get_settings_controller() {
10
+ static $instance = null;
11
+ return is_null( $instance )
12
+ ? $instance = new Aventura\Wprss\Core\Licensing\Settings()
13
+ : $instance;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  }
15
 
16
 
 
17
  /**
18
+ * Gets the singleton instance of the Manager class, creating it if it doesn't exist.
19
  *
20
+ * @since 4.7.8
21
+ * @return Aventura\Wprss\Core\Licensing\Manager
22
  */
23
+ function wprss_licensing_get_manager() {
24
+ static $manager = null;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
 
26
+ if ( is_null( $manager ) ) {
27
+ $manager = new Aventura\Wprss\Core\Licensing\Manager();
28
+ $manager->setExpirationNoticePeriod( wprss_get_general_setting( 'expiration_notice_period' ) );
29
+ $manager->setDefaultAuthorName( 'Jean Galea' );
30
+ }
 
 
 
31
 
32
+ return $manager;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  }
34
 
 
 
35
  /**
36
+ * Gets the singleton instance of the AjaxController class, creating it if it doesn't exist.
37
  *
38
+ * @since 4.7.8
39
+ * @return Aventura\Wprss\Core\Licensing\AjaxController
40
  */
41
+ function wprss_licensing_get_ajax_controller() {
42
+ static $instance = null;
43
+ return is_null( $instance )
44
+ ? $instance = new Aventura\Wprss\Core\Licensing\AjaxController()
45
+ : $instance;
 
 
 
 
 
 
 
 
46
  }
47
 
 
48
  /**
49
+ * Returns all registered addons.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  *
51
  * @since 4.4.5
52
+ * @param bool $noCache If true, the add-ons filter will be ran again; if false, it will only be ran if not ran before.
53
+ * @return array Array of add-on codes.
54
  */
55
+ function wprss_get_addons($noCache = false) {
56
+ static $addons = null;
57
+ return is_null( $addons ) || $noCache
58
+ ? $addons = apply_filters( 'wprss_register_addon', array() )
59
+ : $addons;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  }
61
 
 
 
62
  /**
63
+ * Hooks the licensing system into WordPress.
 
 
64
  */
65
+ function wprss_init_licensing() {
66
+ // Get licensing class instances
67
+ $manager = wprss_licensing_get_manager();
68
+ $settingsController = wprss_licensing_get_settings_controller();
69
+ $ajaxController = wprss_licensing_get_ajax_controller();
70
 
71
+ // Set up Ajax Controller pointers
72
+ $ajaxController->setManager( $manager );
73
+ $ajaxController->setSettingsController( $settingsController );
74
 
75
+ // Licensing Manager hooks
76
+ add_action( 'admin_init', array( $manager, 'initUpdaterInstances' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
 
78
+ // Licensing Ajax Controller hooks
79
+ add_action( 'wp_ajax_wprss_ajax_manage_license', array( $ajaxController, 'handleAjaxManageLicense' ) );
80
+ add_action( 'wp_ajax_wprss_ajax_fetch_license', array( $ajaxController, 'handleAjaxFetchLicense' ) );
81
 
82
+ // Licensing Settings Controller hooks
83
+ add_action( 'wprss_admin_init', array( $settingsController, 'registerSettings' ), 100 );
84
+ add_action( 'admin_init', array( $settingsController, 'handleLicenseStatusChange' ), 10 );
85
+ add_action( 'wprss_settings_license_key_is_valid', array( $settingsController, 'validateLicenseKeyForSave' ) );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
  }
includes/scripts.php CHANGED
@@ -29,6 +29,7 @@
29
  if ( isset( $_GET['page'] ) && ( $_GET['page'] == 'wprss-aggregator' || $_GET['page'] == 'wprss-aggregator-settings'
30
  || $_GET['page'] == 'wprss-import-export-settings' || $_GET['page'] == 'wprss-debugging' || $_GET['page'] == 'wprss-addons' ) ) {
31
  wp_enqueue_style( 'wprss-styles', WPRSS_CSS . 'admin-styles.css' );
 
32
  }
33
 
34
  if ( is_admin() ) {
29
  if ( isset( $_GET['page'] ) && ( $_GET['page'] == 'wprss-aggregator' || $_GET['page'] == 'wprss-aggregator-settings'
30
  || $_GET['page'] == 'wprss-import-export-settings' || $_GET['page'] == 'wprss-debugging' || $_GET['page'] == 'wprss-addons' ) ) {
31
  wp_enqueue_style( 'wprss-styles', WPRSS_CSS . 'admin-styles.css' );
32
+ wp_enqueue_style( 'wprss-fa', WPRSS_CSS . 'font-awesome.min.css' );
33
  }
34
 
35
  if ( is_admin() ) {
includes/system-info.php CHANGED
@@ -26,12 +26,11 @@
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 ); ?>">
30
- <?php wprss_print_system_info(); ?>
31
  </textarea>
32
  <p class="submit">
33
- <input type="hidden" name="wprss-action" value="download_sysinfo" />
34
- <?php submit_button( __( 'Download System Info File', WPRSS_TEXT_DOMAIN ), 'primary', 'wprss-download-sysinfo', false ); ?>
35
  </p>
36
  </form>
37
 
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
+ <?php submit_button( __( 'Download System Info File', WPRSS_TEXT_DOMAIN ), 'primary', 'wprss-download-sysinfo', false ); ?>
34
  </p>
35
  </form>
36
 
includes/update.php CHANGED
@@ -1,12 +1,12 @@
1
  <?php
2
- /**
3
  * Contains all the functions related to updating the plugin from
4
  * one version to another
5
- *
6
  * @package WP RSS Aggregator
7
  */
8
 
9
- add_action( 'init', 'wprss_version_check' );
10
  /**
11
  * Checks the version number and runs install or update functions if needed.
12
  *
@@ -16,15 +16,15 @@
16
 
17
  // Get the old database version.
18
  $old_db_version = get_option( 'wprss_db_version' );
19
-
20
- // Get the plugin settings.
21
  $settings = get_option( 'wprss_settings' );
22
 
23
- // Get the plugin options
24
- $options = get_option( 'wprss_options' );
25
 
26
  // For fresh installs
27
- // If there is no old database version and no settings, run the install.
28
  if ( empty( $old_db_version ) && false === $settings && false === $options ) {
29
  wprss_install();
30
  }
@@ -34,30 +34,30 @@
34
  elseif ( empty( $old_db_version ) && false === $settings && !empty( $options ) ) {
35
  wp_clear_scheduled_hook( 'wprss_generate_hook' );
36
  wprss_install();
37
- wprss_migrate();
38
- wprss_fetch_insert_all_feed_items();
39
  }
40
 
41
- // For version 1.1 to 3.0
42
  // If there is no old database version, but only settings and options
43
  elseif ( empty( $old_db_version ) && !empty( $settings ) && !empty( $options ) ) {
44
  wp_clear_scheduled_hook( 'wprss_generate_hook' );
45
  wprss_update();
46
  wprss_migrate();
47
- wprss_fetch_insert_all_feed_items();
48
  }
49
 
50
  // For version 2+ to 3.0
51
  // We check if wprss_settings option exists, as this only exists prior to version 3.0
52
  // Settings field changed, and another added
53
- elseif ( intval( $old_db_version ) < intval( WPRSS_DB_VERSION ) && ( FALSE != get_option( 'wprss_settings' ) ) ) {
54
  wprss_upgrade_30();
55
  wprss_update();
56
- wprss_fetch_insert_all_feed_items();
57
  }
58
 
59
- // For any future versions where DB changes
60
- // If the old version is less than the new version, run the update.
61
  elseif ( intval( $old_db_version ) < intval( WPRSS_DB_VERSION ) ) {
62
  wprss_update();
63
  wprss_fetch_insert_all_feed_items();
@@ -70,7 +70,7 @@
70
  $options['follow_dd'] = 'follow';
71
  }
72
  }
73
-
74
  }
75
 
76
 
@@ -81,7 +81,7 @@
81
  */
82
  function wprss_install() {
83
 
84
- // Add the database version setting.
85
  add_option( 'wprss_db_version', WPRSS_DB_VERSION );
86
 
87
  // Add the default plugin settings.
@@ -96,11 +96,11 @@
96
  */
97
  function wprss_update() {
98
 
99
- // Update the database version setting.
100
  update_option( 'wprss_db_version', WPRSS_DB_VERSION );
101
  // Initialize settings
102
  wprss_settings_initialize();
103
-
104
  // Open Link Behavior Name Fix
105
  $settings = get_option( 'wprss_settings_general' );
106
 
@@ -111,7 +111,7 @@
111
  }else if( $settings['open_dd'] === 'Self' || $settings['open_dd'] === __( 'Self', WPRSS_TEXT_DOMAIN ) ){
112
  $settings['open_dd'] = 'self';
113
  }
114
-
115
  // At version 4.7.5 tracking was disabled
116
  $settings['tracking'] = '0';
117
  update_option( 'wprss_settings_general', $settings );
@@ -130,35 +130,35 @@
130
  // Get the default plugin settings.
131
  $default_settings = wprss_get_default_settings_general();
132
 
133
- // Loop through each of the default plugin settings.
134
  foreach ( $default_settings as $setting_key => $setting_value ) {
135
 
136
- // If the setting didn't previously exist, add the default value to the $settings array.
137
  if ( ! isset( $settings[ $setting_key ] ) )
138
  $settings[ $setting_key ] = $setting_value;
139
  }
140
 
141
  // Update the plugin settings.
142
- update_option( 'wprss_settings_general', $settings );
143
  }
144
-
145
 
146
  /**
147
  * Takes care of cron and DB changes between versions 2+ and 3
148
  *
149
  * @since 3.0
150
- */
151
  function wprss_upgrade_30() {
152
- wp_clear_scheduled_hook( 'wprss_fetch_feeds_hook' );
153
 
154
- // Get the settings from the database.
155
  $settings = get_option( 'wprss_settings' );
156
 
157
  // Put them into our new field
158
  update_option( 'wprss_settings_general', $settings );
159
 
160
  // Remove old options field, we are now using wprss_settings_general
161
- delete_option( 'wprss_settings' );
162
  }
163
 
164
 
@@ -166,30 +166,30 @@
166
  * Migrates the feed sources from the wprss_options field to the wp_posts table (for older versions)
167
  *
168
  * @since 2.0
169
- */
170
  function wprss_migrate() {
171
-
172
- // Get the plugin options
173
- $options = get_option( 'wprss_options' );
174
 
175
  $feed_sources = array_chunk( $options, 2 );
176
-
177
- foreach ( $feed_sources as $feed_source ) {
178
  $feed_title = $feed_source[0];
179
  $feed_url = $feed_source[1];
180
-
181
  // Create post object
182
  $feed_item = array(
183
  'post_title' => $feed_title,
184
  'post_content' => '',
185
  'post_status' => 'publish',
186
  'post_type' => 'wprss_feed'
187
- );
188
-
189
- $inserted_ID = wp_insert_post( $feed_item, $wp_error );
190
  // insert post meta
191
- update_post_meta( $inserted_ID, 'wprss_url', $feed_url );
192
- }
193
  // delete unneeded option
194
  delete_option( 'wprss_options' );
195
  }
@@ -209,10 +209,10 @@
209
  // from version 1.1
210
  'open_dd' => 'blank',
211
  'follow_dd' => 'no_follow',
212
-
213
  // from version 2.0
214
- 'feed_limit' => 15,
215
-
216
  // from version 3.0
217
  'date_format' => 'Y-m-d',
218
  'limit_feed_items_db' => 200,
@@ -232,7 +232,7 @@
232
  'custom_feed_url' => 'wprss',
233
  'custom_feed_limit' => '',
234
  'source_link' => 0,
235
-
236
  // from version 3.4
237
  'video_link' => 'false',
238
 
@@ -248,15 +248,18 @@
248
 
249
  // from version 4.1.2
250
  'custom_feed_title' => 'Latest imported feed items on ' . get_bloginfo('name'),
251
-
252
  // From version 4.2.3
253
  'pagination' => 'default',
254
 
255
  // From 4.2.4
256
  'authors_enable' => 0,
257
-
258
  // From 4.7.2
259
  'unique_titles' => 0,
 
 
 
260
  )
261
  );
262
 
@@ -268,7 +271,7 @@
268
 
269
  /**
270
  * Returns the default tracking settings.
271
- *
272
  * @since 3.6
273
  */
274
  function wprss_get_default_tracking_settings() {
1
  <?php
2
+ /**
3
  * Contains all the functions related to updating the plugin from
4
  * one version to another
5
+ *
6
  * @package WP RSS Aggregator
7
  */
8
 
9
+ add_action( 'init', 'wprss_version_check' );
10
  /**
11
  * Checks the version number and runs install or update functions if needed.
12
  *
16
 
17
  // Get the old database version.
18
  $old_db_version = get_option( 'wprss_db_version' );
19
+
20
+ // Get the plugin settings.
21
  $settings = get_option( 'wprss_settings' );
22
 
23
+ // Get the plugin options
24
+ $options = get_option( 'wprss_options' );
25
 
26
  // For fresh installs
27
+ // If there is no old database version and no settings, run the install.
28
  if ( empty( $old_db_version ) && false === $settings && false === $options ) {
29
  wprss_install();
30
  }
34
  elseif ( empty( $old_db_version ) && false === $settings && !empty( $options ) ) {
35
  wp_clear_scheduled_hook( 'wprss_generate_hook' );
36
  wprss_install();
37
+ wprss_migrate();
38
+ wprss_fetch_insert_all_feed_items();
39
  }
40
 
41
+ // For version 1.1 to 3.0
42
  // If there is no old database version, but only settings and options
43
  elseif ( empty( $old_db_version ) && !empty( $settings ) && !empty( $options ) ) {
44
  wp_clear_scheduled_hook( 'wprss_generate_hook' );
45
  wprss_update();
46
  wprss_migrate();
47
+ wprss_fetch_insert_all_feed_items();
48
  }
49
 
50
  // For version 2+ to 3.0
51
  // We check if wprss_settings option exists, as this only exists prior to version 3.0
52
  // Settings field changed, and another added
53
+ elseif ( intval( $old_db_version ) < intval( WPRSS_DB_VERSION ) && ( FALSE != get_option( 'wprss_settings' ) ) ) {
54
  wprss_upgrade_30();
55
  wprss_update();
56
+ wprss_fetch_insert_all_feed_items();
57
  }
58
 
59
+ // For any future versions where DB changes
60
+ // If the old version is less than the new version, run the update.
61
  elseif ( intval( $old_db_version ) < intval( WPRSS_DB_VERSION ) ) {
62
  wprss_update();
63
  wprss_fetch_insert_all_feed_items();
70
  $options['follow_dd'] = 'follow';
71
  }
72
  }
73
+
74
  }
75
 
76
 
81
  */
82
  function wprss_install() {
83
 
84
+ // Add the database version setting.
85
  add_option( 'wprss_db_version', WPRSS_DB_VERSION );
86
 
87
  // Add the default plugin settings.
96
  */
97
  function wprss_update() {
98
 
99
+ // Update the database version setting.
100
  update_option( 'wprss_db_version', WPRSS_DB_VERSION );
101
  // Initialize settings
102
  wprss_settings_initialize();
103
+
104
  // Open Link Behavior Name Fix
105
  $settings = get_option( 'wprss_settings_general' );
106
 
111
  }else if( $settings['open_dd'] === 'Self' || $settings['open_dd'] === __( 'Self', WPRSS_TEXT_DOMAIN ) ){
112
  $settings['open_dd'] = 'self';
113
  }
114
+
115
  // At version 4.7.5 tracking was disabled
116
  $settings['tracking'] = '0';
117
  update_option( 'wprss_settings_general', $settings );
130
  // Get the default plugin settings.
131
  $default_settings = wprss_get_default_settings_general();
132
 
133
+ // Loop through each of the default plugin settings.
134
  foreach ( $default_settings as $setting_key => $setting_value ) {
135
 
136
+ // If the setting didn't previously exist, add the default value to the $settings array.
137
  if ( ! isset( $settings[ $setting_key ] ) )
138
  $settings[ $setting_key ] = $setting_value;
139
  }
140
 
141
  // Update the plugin settings.
142
+ update_option( 'wprss_settings_general', $settings );
143
  }
144
+
145
 
146
  /**
147
  * Takes care of cron and DB changes between versions 2+ and 3
148
  *
149
  * @since 3.0
150
+ */
151
  function wprss_upgrade_30() {
152
+ wp_clear_scheduled_hook( 'wprss_fetch_feeds_hook' );
153
 
154
+ // Get the settings from the database.
155
  $settings = get_option( 'wprss_settings' );
156
 
157
  // Put them into our new field
158
  update_option( 'wprss_settings_general', $settings );
159
 
160
  // Remove old options field, we are now using wprss_settings_general
161
+ delete_option( 'wprss_settings' );
162
  }
163
 
164
 
166
  * Migrates the feed sources from the wprss_options field to the wp_posts table (for older versions)
167
  *
168
  * @since 2.0
169
+ */
170
  function wprss_migrate() {
171
+
172
+ // Get the plugin options
173
+ $options = get_option( 'wprss_options' );
174
 
175
  $feed_sources = array_chunk( $options, 2 );
176
+
177
+ foreach ( $feed_sources as $feed_source ) {
178
  $feed_title = $feed_source[0];
179
  $feed_url = $feed_source[1];
180
+
181
  // Create post object
182
  $feed_item = array(
183
  'post_title' => $feed_title,
184
  'post_content' => '',
185
  'post_status' => 'publish',
186
  'post_type' => 'wprss_feed'
187
+ );
188
+
189
+ $inserted_ID = wp_insert_post( $feed_item, $wp_error );
190
  // insert post meta
191
+ update_post_meta( $inserted_ID, 'wprss_url', $feed_url );
192
+ }
193
  // delete unneeded option
194
  delete_option( 'wprss_options' );
195
  }
209
  // from version 1.1
210
  'open_dd' => 'blank',
211
  'follow_dd' => 'no_follow',
212
+
213
  // from version 2.0
214
+ 'feed_limit' => 15,
215
+
216
  // from version 3.0
217
  'date_format' => 'Y-m-d',
218
  'limit_feed_items_db' => 200,
232
  'custom_feed_url' => 'wprss',
233
  'custom_feed_limit' => '',
234
  'source_link' => 0,
235
+
236
  // from version 3.4
237
  'video_link' => 'false',
238
 
248
 
249
  // from version 4.1.2
250
  'custom_feed_title' => 'Latest imported feed items on ' . get_bloginfo('name'),
251
+
252
  // From version 4.2.3
253
  'pagination' => 'default',
254
 
255
  // From 4.2.4
256
  'authors_enable' => 0,
257
+
258
  // From 4.7.2
259
  'unique_titles' => 0,
260
+
261
+ // From 4.7.8
262
+ 'expiration_notice_period' => '2 weeks',
263
  )
264
  );
265
 
271
 
272
  /**
273
  * Returns the default tracking settings.
274
+ *
275
  * @since 3.6
276
  */
277
  function wprss_get_default_tracking_settings() {
readme.txt CHANGED
@@ -1,703 +1,1412 @@
1
- === WP RSS Aggregator ===
2
- Contributors: jeangalea, Mekku, xedin.unknown, markzahra, doytch, chiragswadia
3
- Plugin URI: http://www.wprssaggregator.com
4
- Tags: rss, feeds, aggregation, rss to post, autoblog aggregator, rss import, feed aggregator, rss aggregator, multiple rss feeds, multi rss feeds, rss multi importer, feed import, feed import, multiple feed import, feed aggregation, rss feader, feed reader, feed to post, multiple feeds, multi feed importer, multi feed import, multi import, autoblogging, autoblogger, rss feeder, rss post importer, autoblog aggregator, autoblog, autopost, content curation, feedwordpress, wp rss multi import, hungryfeed, wp-o-matic, rss feed, rss feed to post, rss retriever, syndication
5
- Requires at least: 4.0
6
- Tested up to: 4.3.1
7
- Stable tag: 4.7.7
8
- License: GPLv2 or later
9
- The no.1 RSS feed importer for WordPress. Premium add-ons available for more functionality.
10
-
11
-
12
- == Description ==
13
-
14
- WP RSS Aggregator is the most comprehensive and elegant RSS feed solution for WordPress.
15
-
16
- The original and premier plugin for importing, merging and displaying RSS and Atom feeds on your WordPress site.
17
-
18
- With WP RSS Aggregator, you can:
19
-
20
- * Display feeds from one or more sites on your blog
21
- * Aggregate feeds from multiple sites
22
-
23
- You can add any number of feeds through an administration panel, the plugin will then pull feed items from these sites, merge them and display them in date order.
24
-
25
- To [display your imported feed items](http://wordpress.org/plugins/wp-rss-aggregator/screenshots/), you can use a shortcode or call the display function directly from within your theme.
26
-
27
- = Highlighted Features =
28
-
29
- * Export a custom RSS feed based on your feed sources
30
- * Pagination
31
- * Set the feed import time interval
32
- * Scheduling of feed imports by feed source
33
- * Various shortcode parameters you can use to further customize the output
34
- * Choose whether to show/hide sources and dates
35
- * Choose the date format
36
- * Set the links as no-follow or not, or add no follow to meta tag
37
- * Select how you would like the links to open (in a Lightbox, a new window, or the current window)
38
- * Set the name of the feed source
39
- * Select number of posts per feed you want to show and store
40
- * Opens YouTube, DailyMotion and Vimeo videos directly
41
- * Limit number of feed items stored in the database
42
- * Feed autodiscovery, which lets you add feeds without even knowing the exact URL.
43
- * Extendable via action and filter hooks
44
- * Integrated with the Simplepie library that come with WordPress. This includes RSS 0.91 and RSS 1.0 formats, the popular RSS 2.0 format, Atom etc.
45
-
46
- = Premium Add-Ons =
47
- Add-Ons that add more functionality to the core plugin are [available for purchase](http://www.wprssaggregator.com/extensions/).
48
-
49
- * [Feed to Post](http://www.wprssaggregator.com/extensions/feed-to-post) - an advanced importer that lets you import RSS to posts or custom post types. Populate a website in minutes (autoblog). This is the most popular extension.
50
- * [Keyword Filtering](http://www.wprssaggregator.com/extensions/keyword-filtering) - filter imported feeds based on keywords, so you only get items you're interested in.
51
- * [Excerpts & Thumbnails](http://www.wprssaggregator.com/extensions/excerpts-thumbnails) - display excerpts and thumbnails together with the title, date and source.
52
- * [Categories](http://www.wprssaggregator.com/extensions/categories) - categorise your feed sources and display items from a particular category at will within your site.
53
- * [WordAi](http://www.wprssaggregator.com/extension/wordai/) - WordAi allows users to take an RSS feed and turn it into new content that is both completely unique and completely readable.
54
- * [Full Text RSS Feeds](http://www.wprssaggregator.com/extension/full-text-rss-feeds/) - connectivity to our Full Text Premium service, which gives you unlimited feed items returned per feed source.
55
-
56
- We also provide a [Feed Creator](http://createfeed.wprssaggregator.com) service, that allows you to generate RSS feeds from any webpage, even if it doesn't have its own RSS feed.
57
-
58
- = Demo =
59
- The core plugin can be seen in use on the [demo page](http://www.wprssaggregator.com/demo/).
60
-
61
- = Video Walkthrough =
62
- [youtube http://www.youtube.com/watch?v=fcENPsmJbvc]
63
-
64
- = Documentation =
65
- Instructions for plugin usage are available on the plugin's [documentation page](http://www.wprssaggregator.com/documentation/).
66
-
67
- = As featured on =
68
- * [Latest WP](http://www.latestwp.com/2015/03/15/wp-rss-aggregator-plugin-review/)
69
- * [WP Beginner](http://www.wpbeginner.com/plugins/how-to-fetch-feeds-in-wordpress-using-wp-rss-aggregator/)
70
- * [WPEXplorer](http://www.wpexplorer.com/custom-rss-aggregator-plugin/)
71
- * [WP Kube](http://www.wpkube.com/wp-rss-aggregator-wordpress-review/)
72
- * [Torquemag](http://torquemag.io/wp-rss-aggregator-review-do-more-with-rss-feeds/)
73
- * [MyWPExpert](http://www.mywpexpert.com/wordpress-rss-aggregator-plugin)
74
- * [Kikolani](http://kikolani.com/create-latest-posts-portfolio-page-wp-rss-aggregator.html)
75
- * [ManageWP Plugins of the Month](http://managewp.com/free-wordpress-plugins-march-2014)
76
- * [TidyRepo](http://tidyrepo.com/wp-rss-aggregator/)
77
- * [WP Eka](http://www.wpeka.com/wp-rss-aggregators-plugin.html)
78
- * [IndexWP](www.indexwp.com/wp-rss-aggregator-plugin-review/)
79
- * [WPulsar](http://www.wpulsar.com/wp-rss-aggregator-plugin-feed-to-posts-keyword-filtering-review/)
80
- * [Kevin Muldoon](http://www.kevinmuldoon.com/wp-rss-aggregator-wordpress-plugin/)
81
-
82
- = Translations =
83
- * Italian - Davide De Maestri
84
- * Spanish - Andrew Kurtis
85
- * Brazilian Portugese - Bruno Calheira
86
- * Dutch - Erick Suiker
87
-
88
- == Installation ==
89
-
90
- 1. Upload the `wp-rss-aggregator` folder to the `/wp-content/plugins/` directory
91
- 2. Activate the WP RSS Aggregator plugin through the 'Plugins' menu in WordPress
92
- 3. Configure the plugin by going to the `RSS Aggregator` menu item that appears in your dashboard menu.
93
- 3. Use the shortcode in your posts or pages: `[wp-rss-aggregator]`
94
-
95
- The parameters accepted are:
96
-
97
- * links_before
98
- * links_after
99
- * link_before
100
- * link_after
101
- * limit
102
- * source
103
- * exclude
104
- * pagination
105
-
106
- An example of a shortcode with parameters:
107
- `[wp_rss_aggregator link_before='<li class="feed-link">' link_after='</li>']`
108
- It is advisable to use the 'HTML' view of the editor when inserting the shortcode with paramters.
109
-
110
- For a full list of shortcode parameters and usage guide please refer to the [documentation](http://www.wprssaggregator.com/docs/shortcodes/).
111
-
112
- __Usage within theme files__
113
-
114
- An example of a function call from within the theme's files:
115
- `
116
- <?php
117
- wprss_display_feed_items( $args = array(
118
- 'links_before' => '<ul>',
119
- 'links_after' => '</ul>',
120
- 'link_before' => '<li>',
121
- 'link_after' => '</li>',
122
- 'limit' => '8',
123
- 'source' => '5,9'
124
- ));
125
- ?>
126
- `
127
-
128
- OR
129
-
130
- `<?php do_shortcode('[wp-rss-aggregator]'); ?>`
131
-
132
-
133
- == Frequently Asked Questions ==
134
- = How do I display the imported feed items? =
135
-
136
- You can either use the shortcode in your posts and pages:
137
- `[wp-rss-aggregator]`
138
-
139
- or you can call the function directly within your theme:
140
- `<?php wprss_display_feed_items(); ?>`
141
-
142
- = Is there a limit on the number of feed sources I can use? =
143
-
144
- There is no limit in place for the number of feed sources. Having many (50+) feed sources should not present any problems in itself.
145
-
146
- However, pulling in posts from many sites is bound to put your server under some stress, so you might want to consider using a hosting solution that goes beyond your typical shared host.
147
-
148
- Check out our dedicated page for hosting recommendations.
149
-
150
- = Does WP RSS Aggregator work using JSON as the source? =
151
-
152
- No, our plugin does not currently import from JSON, it only imports from RSS and Atom structured XML.
153
-
154
- = Why do I get “No feed items found” when I insert the shortcode on a page or post? =
155
-
156
- Try adding a few more feed sources and make sure they are valid by using the RSS Feed validator.
157
-
158
- Secondly make sure your WordPress cron system is working well. If not, the feeds cannot be imported. If in doubt you can go to RSS Aggregator > Debugging and hit the red button to re-import all feed items. If the problem persists contact support.
159
-
160
- = Can I store imported feed items as posts? =
161
-
162
- Yes! You can do that with the [Feed to Post](http://www.wprssaggregator.com/extensions/feed-to-post) add-on. You will not only be able to store items as posts, but also as other custom post types, as well as set the author, auto set tags and categories, import images into the gallery or set featured images, and much more.
163
-
164
- = Some RSS feeds only give a short excerpt. Any way around that? =
165
-
166
- Yes, along with the [Feed to Post](http://www.wprssaggregator.com/extensions/feed-to-post) add-on we have another add-on called [Full Text RSS Feeds](http://www.wprssaggregator.com/extension/full-text-rss-feeds/) that can get the full content of those feeds that only supply a short excerpt.
167
-
168
- = I’m not sure which premium add-ons are right for me. Can you help me out? =
169
-
170
- Sure! We wrote a post just for you. Read about which add-ons you should buy, we explain the different types of usage so you’ll know what to expect when purchasing.
171
-
172
- If you need any further help you can contact our support team [here](http://www.wprssaggregator.com/contact/).
173
-
174
- = Where can I find the documentation for the plugin? =
175
-
176
- The full documentation section can be found on the [WP RSS Aggregator website](www.wprssaggregator.com/documentation/), the documentation also includes an extensive FAQ list.
177
-
178
-
179
- == Screenshots ==
180
-
181
- 1. Feed items imported by WP RSS Aggregator displayed on the front-end using the shortcode.
182
-
183
- 2. Feed Items imported by WP RSS Aggregator and displayed with the [Excerpts & Thumbnails](http://www.wprssaggregator.com/extensions/excerpts-thumbnails) add-on installed.
184
-
185
- 3. Adding/Editing a feed source.
186
-
187
- 4. The feed sources.
188
-
189
- 5. The imported feeds items.
190
-
191
- 6. WP RSS Aggregator's Settings page.
192
-
193
-
194
- == Changelog ==
195
-
196
- = 4.7.7 (2015-10-19) =
197
- * Enhanced: Optimized checking for plugin updates.
198
-
199
- = 4.7.6 (2015-10-07) =
200
- * Enhanced: Feeds that fail to validate due to whitespace at the beginning are now supported by the plugin.
201
- * Fixed bug: Undefined variables in the System Info section in the Debugging page.
202
- * Fixed bug: Add-on license expiration notices could not be dismissed.
203
-
204
- = 4.7.5 (2015-09-02) =
205
- * Usage tracking now disabled.
206
- * Fixed bug: error related to undefined `ajaxurl` JS variable gone from frontend.
207
- * Enhanced: Licensing errors will be output to debug log.
208
- * Enhanced: Improved compatibility with plugins that allow AJAX searching in the backend.
209
-
210
- = 4.7.4 (2015-08-20) =
211
- * Requirement: WordPress 4.0 or greater now required.
212
- * Fixed bug in image caching
213
- * Fixed bug in admin interface due to incorrectly translated IDs
214
-
215
- = 4.7.3 (2015-08-04) =
216
- * Enhanced: Core now implements an image cache logic.
217
- * Enhanced: Add-ons on the "Add-ons" page now have an installed-but-inactive status.
218
- * Enhanced: Google Alerts permalinks will now be normalized.
219
- * Enhanced: Russian translation added.
220
- * Fixed bug: Inline help (tooltips) translations now work.
221
- * Fixed bug: Link to the Feed to Post add-on on the welcome page is no longer broken.
222
-
223
- = 4.7.2 (2015-06-30) =
224
- * Enhanced: Copyright updated.
225
- * Fixed bug: Word trimming no longer adds extra closing tags at the end.
226
- * Fixed bug: Presence of `idna_convert` class no longer causes infinite redirects on some servers.
227
- * Fixed bug: Warning of unterminated comment no longer thrown in PHP 5.5.
228
- * Fixed bug: Added default value for "Unique Titles" option.
229
- * Fixed bug: Having a the port number specified with the database host no longer causes issues with the `mysqli` adapter in System Info on some servers.
230
- * Fixed bug: Nested options of inline help controller no longer cause a fatal error.
231
- * Fixed bug: Notices will no longer be displayed during rendering of feed items due to absence of required default values.
232
-
233
- = 4.7.1 (2015-04-23) =
234
- * Fixed bug: No warning will be thrown when fetching feeds.
235
-
236
- = 4.7 (2015-04-21) =
237
- * New: Optionally import only items with titles that don't already exist.
238
- * Enhanced: Accessing feeds over HTTPS is now possible.
239
- * Enhanced: Added support for multibyte strings in some places.
240
- * Enhanced: Increased JS compatibility with other plugins.
241
- * Enhanced: Increased UI support for mobile devices.
242
- * Fixed bug: Having no mysqli extension no longer causes an error to appear in the debug info.
243
- * Fixed bug: Saving an empty license key no longer results in a warning.
244
-
245
- = 4.6.13 (2015-03-20) =
246
- * Fixed bug: The "Force feed" option wasn't being correctly used.
247
-
248
- = 4.6.12 (2015-03-09) =
249
- * Fixed bug: The "Force feed" option was being removed by the Feed to Post add-on.
250
-
251
- = 4.6.11 (2015-03-04) =
252
- * Enhanced: The Help page now includes a support form if a premium add-on is detected.
253
- * Enhanced: Updated some translations for admin options.
254
- * Fixed bug: Help tooltips are now optimized for iPad screens.
255
- * Fixed bug: Errors on the licensing page when a license code has not yet been entered.
256
-
257
- = 4.6.10 (2015-02-10) =
258
- * Enhanced: AJAX license activation.
259
- * Enhanced: License form more reliable.
260
- * Enhanced: license-related UI improvements
261
- * New: Markdown library added. Changelog now read from readme.
262
- * Fixed bug: Saving license keys not longer triggers error in some cases.
263
-
264
- = 4.6.9 (2015-01-21) =
265
- * Enhanced: Admin user will now be warned about invalid or expiring licenses.
266
- * Enhanced: Admin notices logic centralized in this plugin.
267
- * Fixed: Multiple small-scale security vulnerabilities.
268
- * Fixed: Ampersand in feed URL no longer causes the product of generated feeds to be invalidated by W3C Validator.
269
-
270
- = 4.6.8 (2015-01-07) =
271
- * Enhanced: Added more logging during feed importing.
272
- * Enhanced: Irrelevent metaboxes added by other plugins are now removed from the Add/Edit Feed Source page.
273
- * Fixed bug: Valid feed URLS were being invalidated.
274
- * Fixed bug: The Blacklist feature was being hidden when the Feed to Post add-on was enabled.
275
- * Fixed bug: Patched a vulnerability where any user on the site can issue a feed fetch.
276
- * Fixed bug: The "Activate" and "Pause" actions are not shown in the bulk actions dropdown in WordPress v4.1.
277
-
278
- = 4.6.7 (2014-12-17) =
279
- * Enhanced: Some minor interface updates.
280
- * Enhanced: Added filters for use by the premium add-ons.
281
-
282
- = 4.6.6 (2014-12-06) =
283
- * Enhanced: Added output layouts for feed sources and feed items.
284
- * Enhanced: Updated EDD updater class to version 1.5.
285
- * Enhanced: Added time limit extending to prevent script from exhausting its execution time limit while importing.
286
- * Fixed bug: The "Delete and Re-import" button was deleting items but not re-importing.
287
- * Fixed bug: Non-object errors when a feed source is deleted while importing.
288
-
289
- = 4.6.5 (2014-11-17) =
290
- * Enhanced: Improved the logging.
291
- * Enhanced: Improved the licensing fields.
292
- * Enhanced: Updated the EDD updater class to the latest version.
293
- * Fixed bug: Small random error when viewing the licenses page.
294
-
295
- = 4.6.4 (2014-11-10) =
296
- * Enhanced: Added filters to the custom feed.
297
- * Enhanced: Updated some styles to improve the user interface.
298
- * Fixed bug: The "Remove selected from Blacklist" button had no nonce associated with it.
299
- * Fixed bug: The Blacklist menu entry was not always being shown.
300
-
301
- = 4.6.3 (2014-11-3) =
302
- Enhanced: Re-added the "Add New" link in the plugin's menu.
303
- Enhanced: Improved error logging.
304
- Enhanced: Bulk actions in the Feed Sources page are now also included in the bottom dropdown menu.
305
- Fixed bug: Add-on updater was prone to conflicts. Now enclosed in an action.
306
- Fixed bug: The Full Text RSS Feeds add-on was not showing as active in the "Add-ons" page.
307
- Fixed bug: Broken links in the "Add-ons" page, to add-on pages on our site.
308
-
309
- = 4.6.2 (2014-10-15) =
310
- * Enhanced: Improved plugin responsiveness.
311
- * Enhanced: Updated some help text in tooltips with better explainations and added clarity.
312
- * Enhanced: Optimized some old SQL queries.
313
- * Enhanced: Added better debug logging.
314
- * Enhanced: Added a new filter to modify the text shown before author names.
315
- * Fixed bug: Licenses were not showing as active, even though they were activated.
316
-
317
- = 4.6.1 (2014-10-06) =
318
- * Enhanced: Improved internationalization in the plugin, for better translations.
319
- * Fixed bug: If the feed source age limit was left empty, the global setting was used instead of ignoring the limit.
320
-
321
- = 4.6 (2014-09-22) =
322
- * Enhanced: Improved the user interface, with better responsiveness and tooltips.
323
- * Enhanced: Removes the ID column. The ID is now shown fixed in row actions.
324
- * Enhanced: Feed Preview indicates if feed items have no dates.
325
- * Fixed bug: If a feed item has no date, the date and time it was imported is used.
326
-
327
- = 4.5.3 (2014-09-15) =
328
- * New Featured: Added filter to allow adding RSS feeds to the head of your site's pages for CPTs.
329
- * Enhanced: Columns in the feed sources table are now sortable.
330
- * Enhanced: Removed the ID column in the feed sources table. The ID has been moved as a row action.
331
- * Enhanced: Improved various interface elements.
332
- * Enhanced: Better responsiveness for smaller screen.
333
- * Fixed bug: The importing spinning icon would get stuck and spin for a very long time.
334
- * Fixed bug: Removed an old description meta field.
335
- * Fixed bug: Plugin was not removing all scheduled cron jobs when deactivated.
336
-
337
- = 4.5.2 (2014-09-09) =
338
- * Enhanced: Optimized plugin for WordPress 4.0.
339
- * Enhanced: Improved template and added filters for add-on hooking.
340
- * Fixed bug: Editor toolbar visible over the WP RSS shortcode dialog.
341
-
342
- = 4.5.1 (2014-08-26) =
343
- * Fixed bug: Last import feed item count stays at zero.
344
- * Fixed bug: Datetime::setTimestamp error when using PHP 5.2 or earlier.
345
- * Fixed bug: The display limit was not working.
346
- * Fixed bug: Minor bug in licensing.
347
-
348
- = 4.5 (2014-08-25) =
349
- * New Feature: Bulk importer allows you to create multiple feed sources at once.
350
- * Enhanced: Improved OPML importer with added hooks.
351
- * Enhanced: Centralized add-on licensing, fixing multiple bugs.
352
- * Fixed bug: Undefined `feed_limit` errors when using the shortcode.
353
-
354
- = 4.4.4 (2014-08-19) =
355
- * Fixed bug: Errors when using older PHP versions 5.3 or lower.
356
-
357
- = 4.4.3 (2014-08-19) =
358
- * Fixed bug: Errors when using older PHP versions 5.3 or lower.
359
-
360
- = 4.4.2 (2014-08-19) =
361
- * Fixed bug: Errors when using older PHP versions 5.3 or lower.
362
-
363
- = 4.4.1 (2014-08-18) =
364
- * Enhanced: Various improvements to the plugin interface and texts.
365
- * Enhanced: Moved the restore default settings button farther down the Debugging page, to avoid confusion with the delete button.
366
- * Fixed bug: Feed item dates were not being adjusted to the timezone when using a GMT offset.
367
- * Fixed bug: Feed item dates are now adjusted according to daylight savings time.
368
-
369
- = 4.4 (2014-08-11) =
370
- * New Feature: Blacklist - delete items and blacklist them to never import them again.
371
- * Enhanced: Added a button in the Debugging page to reset the plugin settings to default.
372
- * Enhanced: WordPress Yoast SEO metaboxes and custom columns will no longer appear.
373
-
374
- = 4.3.1 (2014-08-08) =
375
- * Enhanced: Better wording on settings page.
376
- * Fixed bug: The Links Behaviour option in the settings was not working.
377
- * Fixed bug: The wrong feed items were being shown for some sources when using the "View Items" row action.
378
-
379
- = 4.3 (2014-08-04) =
380
- * New Feature: Feed items now also import authors.
381
- * Enhanced: Custom feed is now in RSS 2.0 format.
382
- * Enhanced: Improved the display template for feed items.
383
- * Fixed bug: Custom feed was not working in Firefox.
384
- * Fixed bug: Some feed items were showing items from another feed source.
385
- * Fixed bug: The feed limit in the global settings was not working.
386
-
387
- = 4.2.3 (2014-07-29) =
388
- * Enhanced: Added an option to choose between the current pagination type, and numbered pagination.
389
- * Enhanced: The Feed Preview now also shows the total number of items in the feed.
390
- * Fixed bug: A PHP warning error was being shown in the System Info.
391
- * Fixed bug: Language files were not always being referenced correctly.
392
- * Fixed bug: Manually fetching a feed fails if the feed is scheduled to update in the next 10 minutes.
393
- * Fixed bug: Bing RSS feeds were importing duplicates on every update.
394
-
395
- = 4.2.2 (2014-07-23) =
396
- * Enhanced: Facebook page feeds are now changed into RSS 2.0 feeds, rather than Atom 1.0 feeds.
397
- * Enhanced: Improved live updating performace on the Feed Sources page.
398
-
399
- = 4.2.1 (2014-07-17) =
400
- * Enhanced: Feed Sources page is now more responsive.
401
-
402
- = 4.2 (2014-07-17) =
403
- * New Feature: Can now view each feed source's imported feed items separate from other feed sources' feed items.
404
- * Enhanced: Major visual update to the Feed Sources page with new live updates.
405
- * Enhanced: The custom feed now includes the feed source.
406
- * Fixed bug: Google News feeds were importing duplicate items on every update.
407
- * Fixed bug: Multiple minor bug fixes with old filters.
408
-
409
- = 4.1.6 (2014-06-28) =
410
- * Fixed bug: Results returned by wprss_get_feed_items_for_source() will no longer be affected by filters.
411
- * Fixed bug: Charset issue in titles
412
-
413
- = 4.1.5 (2014-06-19) =
414
- * Enhanced: The Feed Sources table now indicates which feed sources encountered errors during the last import.
415
- * Fixed bug: Feed titles were not being decoded for HTML entities.
416
-
417
- = 4.1.4 (2014-05-16) =
418
- * Enhanced: Minor improvements to feed importing and handling.
419
- * Fixed bug: HTML entities were not being decoded in feed item titles.
420
-
421
- = 4.1.3 (2014-04-28) =
422
- * Enhanced: Added a force feed option, for valid RSS feeds with incorrect header content types.
423
- * Fixed bug: HTML entities in feed item titles are now being decoded.
424
-
425
- = 4.1.2 (2014-04-22) =
426
- * Enhanced: Improved the custom feed, by allowing a custom title.
427
- * Enhanced: Improved shortcode, by adding the "pagination" parameter.
428
- * Enhanced: Modified a filter to fix some bugs in the add-ons.
429
-
430
- = 4.1.1 (2014-04-09) =
431
- * Enhanced: Tracking notices only appear for admin users.
432
- * Fixed bug: Auto Feed Discovery was not working.
433
-
434
- = 4.1 (2014-04-03) =
435
- * New Feature: Feed items can now link to enclosure links in the feed.
436
- * Enhanced: Added a filter to allow add-ons to modify feed item queries.
437
-
438
- = 4.0.9 (2014-03-27) =
439
- * Enhanced: Added a filter to modify the feeds template.
440
- * Fixed bug: Nested lists in feeds template.
441
-
442
- = 4.0.8 (2014-03-20) =
443
- * Fixed bug: Using the shortcode makes the comments section always open.
444
-
445
- = 4.0.7 (2014-03-08) =
446
- * Fixed bug: The plugin prevented uploading of header images.
447
-
448
- = 4.0.6 (2014-03-05) =
449
- * Fixed bug: Hook change in last version suspected reason for some installations having non-updated feed items.
450
-
451
- = 4.0.5 (2014-03-03) =
452
- * New Feature: Time ago added as an option.
453
- * Enhanced: The plugin now allows the use of RSS and Atom feeds that do not specify the correct MIME type.
454
- * Enhanced: Better performance due to better hook usage.
455
- * Fixed bug: Facebook page feed URL conversion was not being triggered for new feed sources.
456
- * Fixed bug: Styles fix for pagination.
457
- * Fixed bug: Removed empty spaces in logging.
458
-
459
- = 4.0.4 (2014-02-17) =
460
- * Enhanced: Added Activate/Pause bulk actions in the Feed Sources page.
461
- * Enhanced: Feed Sources page table has been re-designed.
462
- * Enhanced: Logging is now site dependant on multisite.
463
- * Fixed bug: Undefined display settings where appearing on the front end.
464
-
465
- = 4.0.3 (2014-02-12) =
466
- * Fixed bug: The general setting for deleting feed items by age was not working.
467
-
468
- = 4.0.2 (2014-02-10) =
469
- * Enhanced: Added a filter to change the html tags allowed in feed item content.
470
-
471
- = 4.0.1 (2014-02-08) =
472
- * Fixed bug: Empty array of feed items bug caused importing problems.
473
-
474
- = 4.0 (2014-02-04) =
475
- * Enhanced: Improved some internal queries, for better performance.
476
- * Fixed bug: Feed limits were not working properly.
477
-
478
- = 3.9.9 (2014-02-03) =
479
- * Enhanced: The custom feed can now be extended by add-ons.
480
-
481
- = 3.9.8 (2014-01-20) =
482
- * Fixed bug: Removed excessive logging from Debugging Error Log.
483
-
484
- = 3.9.7 (2014-01-17) =
485
- * Fixed bug: Bug in admin-debugging.php causing trouble with admin login
486
-
487
- = 3.9.6 (2014-01-17) =
488
- * Enhanced: Added error logging.
489
-
490
- = 3.9.5 (2014-01-02) =
491
- * Enhanced: Added a feed validator link in the New/Edit Feed Sources page.
492
- * Enhanced: The Next Update column also shows the time remaining for next update, for feed source on the global update interval.
493
- * Enhanced: The custom feed has been improved, and is now identical to the feeds displayed with the shortcode.
494
- * Enhanced: License notifications only appear on the main site when using WordPress multisite.
495
- * Enhanced: Updated Colorbox script to 1.4.33
496
- * Fixed bug: The Imported Items column was always showing zero.
497
- * Fixed bug: Feed items not being imported with limit set to zero. Should be unlimited.
498
- * Fixed bug: Fat header in Feed Sources page
499
-
500
- = 3.9.4 (2013-12-24) =
501
- * Enhanced: Added a column in the Feed Sources page that shows the number of feed items imported for each feed source.
502
- * Fixed bug: Leaving the delete old feed items empty did not ignore the delete.
503
-
504
- = 3.9.3 (2013-12-23) =
505
- * Fixed bug: Fixed tracking pointer appearing on saving settings.
506
-
507
- = 3.9.2 (2013-12-21) =
508
- * Fixed bug: Incorrect file include call.
509
-
510
- = 3.9.1 (2013-12-12) =
511
- * Enhanced: Improved date and time handling for imported feed items.
512
- * Fixed bug: Incorrect values being shown in the Feed Processing metabox.
513
- * Fixed bug: Feed limits set to zero were causing feeds to not be imported.
514
-
515
- = 3.9 (2013-12-12) =
516
- * New Feature: Feed sources can have their own update interval.
517
- * New Feature: The time remaining until the next update has been added to the Feed Source table.
518
-
519
- = 3.8 (2013-12-05) =
520
- * New Feature: Feed items can be limited and deleted by their age.
521
- * Enhanced: Added utility functions for shorter filters.
522
- * Fixed bug: License codes were being erased when add-ons were deactivated.
523
- * Fixed bug: Some feed sources could not be set to active from the table controls.
524
- * Fixed bug: str_pos errors appear when custom feed url is left empty.
525
- * Fixed bug: Some options were producing undefined index errors.
526
-
527
- = 3.7 (2013-11-28) =
528
- * New Feature: State system - Feed sources can be activated/paused.
529
- * New Feature: State system - Feed sources can be set to activate or pause themselves at a specific date and time.
530
- * Enhanced: Added compatibility with nested outline elements in OPML files.
531
- * Enhanced: Admin menu icon image will change into a Dashicon, when WordPress is updated to 3.8 (Decemeber 2013).
532
- * Fixed bug: Custom Post types were breaking when the plugin is activated.
533
-
534
- = 3.6.1 (2013-11-17) =
535
- * Fixed bug: Missing 2nd argument for wprss_shorten_title()
536
-
537
- = 3.6 (2013-11-16) =
538
- * New Feature: Can set the maximum length for titles. Long titles get trimmed.
539
- * Fixed bug: Fixed errors with undefined indexes for unchecked checkboxes in the settings page.
540
- * Fixed bug: Pagination on front static page was not working.
541
-
542
- = 3.5.2 (2013-11-11) =
543
- * Fixed bug: Invalid feed source url was producing an Undefined method notice.
544
- * Fixed bug: Custom feed was producing a 404 page.
545
- * Fixed bug: Presstrends code firing on admin_init, opt-in implementation coming soon
546
-
547
- = 3.5.1 (2013-11-09) =
548
- * Enhanced: Increased compatibility with RSS sources.
549
- * Fixed bug: Pagination not working on home page
550
-
551
- = 3.5 (2013-11-6) =
552
- * New Feature: Can delete feed items for a particular source
553
- * Enhanced: the 'Fetch feed items' row action for feed sources resets itself after 3.5 seconds.
554
- * Enhanced: The feed image is saved for each url.
555
- * Fixed bug: Link to source now links to correct url. Previously linked to site's feed.
556
-
557
- = 3.4.6 (2013-11-1) =
558
- * Enhanced: Added more hooks to debugging page for the Feed to Post add-on.
559
- * Fixed bug: Uninitialized loop index
560
-
561
- = 3.4.5 (2013-10-30) =
562
- * Bug Fix: Feed items were not being imported while the WPML plugin was active.
563
-
564
- = 3.4.4 (2013-10-26) =
565
- * New feature: Pagination
566
- * New feature: First implementation of editor button for easy shortcode creation
567
- * Enhanced: Feed items and sources don't show up in link manager
568
- * Enhanced: Included Presstrends code for plugin usage monitoring
569
-
570
- = 3.4.3 (2013-10-20) =
571
- * Fixed bug: Removed anonymous functions for backwards PHP compatibility
572
- * Bug fix: Added suppress_filters in feed-display.php to prevent a user reported error
573
- * Bug fix: Missing <li> in certain feed displays
574
-
575
- = 3.4.2 (2013-9-19) =
576
- * Enhanced: Added some hooks for Feed to Post compatibility
577
- * Enhanced: Moved date settings to a more appropriate location
578
-
579
- = 3.4.1 (2013-9-16) =
580
- * Fixed Bug: Minor issue with options page - PHP notice
581
-
582
- = 3.4 (2013-9-15) =
583
- * New Feature: Saving/Updating a feed source triggers an update for that source's feed items.
584
- * New Feature: Option to change Youtube, Vimeo and Dailymotion feed item URLs to embedded video players URLs
585
- * New Feature: Facebook Pages URLs are automatically detected and changed into Atom Feed URLs using FB's Graph
586
- * Enhanced: Updated jQuery Colorbox library to 1.4.29
587
- * Fixed Bug: Some settings did not have a default value set, and were throwing an 'Undefined Index' error
588
- * Fixed Bug: Admin notices do not disappear immediately when dismissed.
589
-
590
- = Version 3.3.3 (2013-09-08) =
591
- * Fixed bug: Better function handling on uninstall, should remove uninstall issues
592
-
593
- = Version 3.3.2 (2013-09-07) =
594
- * New feature: Added exclude parameter to shortcode
595
- * Enhanced: Added metabox links to documentation and add-ons
596
- * Fixed bug: Custom feed linking to post on user site rather than original source
597
- * Fixed bug: Custom post types issues when activitating the plugin
598
-
599
- = Version 3.3.1 (2013-08-09) =
600
- * Fixed Bug: Roles and Capabilities file had not been included
601
- * Fixed Bug: Error on install, function not found
602
-
603
- = Version 3.3 (2013-08-08) =
604
- * New feature: OPML importer
605
- * New feature: Feed item limits for individual Feed Sources
606
- * New feature: Custom feed URL
607
- * New feature: Feed limit on custom feed
608
- * New feature: New 'Fetch feed items' action for each Feed Source in listing display
609
- * New feature: Option to enable link to source
610
- * Enhanced: Date strings now change according to locale being used (i.e. compatible with WPML)
611
- * Enhanced: Capabilities implemented
612
- * Enhanced: Feed Sources row action 'View' removed
613
- * Fixed Bug: Proxy feed URLs resulting in the permalink: example.com/url
614
-
615
- = Version 3.2 (2013-07-06) =
616
- * New feature: Parameter to limit number of feeds displayed
617
- * New feature: Paramter to limit feeds displayed to particular sources (via ID)
618
- * Enhanced: Better feed import handling to handle large number of feed sources
619
-
620
- = Version 3.1.1 (2013-06-06) =
621
- * Fixed bug: Incompatibility with some other plugins due to function missing namespace
622
-
623
- = Version 3.1 (2013-06-06) =
624
- * New feature: Option to set the number of feed items imported from every feed (default 5)
625
- * New feature: Import and Export aggregator settings and feed sources
626
- * New feature: Debugging page allowing manual feed refresh and feed reset
627
- * Enhanced: Faster handling of restoring sources from trash when feed limit is 0
628
- * Fixed bug: Limiter on number of overall feeds stored not working
629
- * Fixed bug: Incompatibility issue with Foobox plugin fixed
630
- * Fixed bug: Duplicate feeds sometimes imported
631
-
632
- = Version 3.0 (2013-03-16) =
633
- * New feature: Option to select cron frequency
634
- * New feature: Code extensibility added to be compatible with add-ons
635
- * New feature: Option to set a limit to the number of feeds stored (previously 50, hard coded)
636
- * New feature: Option to define the format of the date shown below each feed item
637
- * New feature: Option to show or hide source of feed item
638
- * New feature: Option to show or hide publish date of feed item
639
- * New feature: Option to set text preceding publish date
640
- * New feature: Option to set text preceding source of feed item
641
- * New feature: Option to link title or not
642
- * New feature: Limit of 5 items imported for each source instead of 10
643
- * Enhanced: Performance improvement when publishing * New feeds in admin
644
- * Enhanced: Query tuning for better performance
645
- * Enhanced: Major code rewrite, refactoring and inclusion of hooks
646
- * Enhanced: Updated Colorbox to v1.4.1
647
- * Enhanced: Better security implementations
648
- * Enhanced: Better feed preview display
649
- * Fixed bug: Deletion of items upon source deletion not working properly
650
- * Requires: WordPress 3.3
651
-
652
- = Version 2.2.3 (2012-11-01) =
653
- * Fixed bug: Tab navigation preventing typing in input boxes
654
- * Removed: Feeds showing up in internal linking pop up
655
-
656
- = Version 2.2.2 (2012-10-30) =
657
- * Removed: Feeds showing up in site search results
658
- * Enhanced: Better tab button navigation when adding a new feed
659
- * Enhanced: Better guidance when a feed URL is invalid
660
-
661
- = Version 2.2.1 (2012-10-17) =
662
- * Fixed bug: wprss_feed_source_order assumes everyone is an admin
663
-
664
- = Version 2.2 (2012-10-01) =
665
- * Italian translation added
666
- * Feed source order changed to alphabetical
667
- * Fixed bug - repeated entries when having a non-valid feed source
668
- * Fixed bug - all imported feeds deleted upon trashing a single feed source
669
-
670
- = Version 2.1 (2012-09-27) =
671
- * Now localised for translations
672
- * Fixed bug with date string
673
- * Fixed $link_before and $link_after, now working
674
- * Added backwards compatibility for wp_rss_aggregator() function
675
-
676
- = Version 2.0 (2012-09-21) =
677
- * Bulk of code rewritten and refactored
678
- * Added install and upgrade functions
679
- * Added DB version setting
680
- * Feed sources now stored as Custom Post Types
681
- * Feed source list sortable ascending or descending by name
682
- * Removed days subsections in feed display
683
- * Ability to limit total number of feeds displayed
684
- * Feeds now fetched via Cron
685
- * Cron job to delete old feed items, keeps max of 50 items in DB
686
- * Now requires WordPress 3.2
687
- * Updated colorbox to v1.3.20.1
688
- * Limit of 15 items max imported for each source
689
- * Fixed issue of page content displaying incorrectly after feeds
690
-
691
- = Version 1.1 (2012-08-13) =
692
- * Now requires WordPress 3.0
693
- * More flexible fetching of images directory
694
- * Has its own top level menu item
695
- * Added settings section
696
- * Ability to open in lightbox, new window or default browser behaviour
697
- * Ability to set links as follow or no follow
698
- * Using constants for oftenly used locations
699
- * Code refactoring
700
- * Changes in file and folder structure
701
-
702
- = Version 1.0 (2012-01-06) =
703
- * Initial release.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ === WP RSS Aggregator ===
2
+ Contributors: jeangalea, Mekku, xedin.unknown, markzahra, doytch, chiragswadia
3
+ Plugin URI: http://www.wprssaggregator.com
4
+ Tags: rss, aggregation, autoblog, autoblog aggregator, autoblogger, autoblogging, autopost, content curation, feed aggregation, feed aggregator, feed import, feed reader, feed to post, feeds, multi feed import, multi feed importer, multi rss feeds, multiple feed import, multiple rss feeds,rss aggregator, rss feader, RSS Feed, rss feed to post, rss feeder, RSS import, rss multi importer, rss post importer, rss retriever, rss to post, syndication
5
+ Requires at least: 4.0
6
+ Tested up to: 4.3.1
7
+ Stable tag: 4.7.8
8
+ License: GPLv2 or later
9
+ The no.1 RSS feed importer for WordPress. Premium add-ons available for more functionality.
10
+
11
+
12
+ == Description ==
13
+
14
+ WP RSS Aggregator is the most comprehensive and elegant RSS feed solution for WordPress.
15
+
16
+ The original and best plugin for importing, merging and displaying RSS and Atom feeds on your WordPress site.
17
+
18
+ With WP RSS Aggregator, you can:
19
+
20
+ * Display feeds from one or more sites on your blog
21
+ * Aggregate feeds from multiple sites
22
+
23
+ You can add any number of feeds through an administration panel, the plugin will then pull feed items from these sites, merge them and display them in date order.
24
+
25
+ To [display your imported feed items](http://wordpress.org/plugins/wp-rss-aggregator/screenshots/), you can use a shortcode or call the display function directly from within your theme.
26
+
27
+ = Highlighted Features =
28
+
29
+ * Export a custom RSS feed based on your feed sources
30
+ * Pagination
31
+ * Set the feed import time interval
32
+ * Scheduling of feed imports by feed source
33
+ * Various shortcode parameters you can use to further customize the output
34
+ * Choose whether to show/hide sources and dates
35
+ * Choose the date format
36
+ * Set the links as no-follow or not, or add no follow to meta tag
37
+ * Select how you would like the links to open (in a Lightbox, a new window, or the current window)
38
+ * Set the name of the feed source
39
+ * Select number of posts per feed you want to show and store
40
+ * Opens YouTube, DailyMotion and Vimeo videos directly
41
+ * Limit number of feed items stored in the database
42
+ * Feed autodiscovery, which lets you add feeds without even knowing the exact URL.
43
+ * Extendable via action and filter hooks
44
+ * Integrated with the Simplepie library that come with WordPress. This includes RSS 0.91 and RSS 1.0 formats, the popular RSS 2.0 format, Atom etc.
45
+
46
+ = Premium Add-Ons =
47
+ Add-Ons that add more functionality to the core plugin are [available for purchase](http://www.wprssaggregator.com/extensions/).
48
+
49
+ * [Feed to Post](http://www.wprssaggregator.com/extensions/feed-to-post) - an advanced importer that lets you import RSS to posts or custom post types. Populate a website in minutes (autoblog). This is the most popular extension.
50
+ * [Keyword Filtering](http://www.wprssaggregator.com/extensions/keyword-filtering) - filter imported feeds based on keywords, so you only get items you're interested in.
51
+ * [Excerpts & Thumbnails](http://www.wprssaggregator.com/extensions/excerpts-thumbnails) - display excerpts and thumbnails together with the title, date and source.
52
+ * [Categories](http://www.wprssaggregator.com/extensions/categories) - categorise your feed sources and display items from a particular category at will within your site.
53
+ * [WordAi](http://www.wprssaggregator.com/extension/wordai/) - WordAi allows users to take an RSS feed and turn it into new content that is both completely unique and completely readable.
54
+ * [Full Text RSS Feeds](http://www.wprssaggregator.com/extension/full-text-rss-feeds/) - connectivity to our Full Text Premium service, which gives you unlimited feed items returned per feed source.
55
+
56
+ We also provide a [Feed Creator](http://createfeed.wprssaggregator.com) service, that allows you to generate RSS feeds from any webpage, even if it doesn't have its own RSS feed.
57
+
58
+ = Demo =
59
+ The core plugin can be seen in use on the [demo page](http://www.wprssaggregator.com/demo/).
60
+
61
+ = Video Walkthrough =
62
+ [youtube http://www.youtube.com/watch?v=fcENPsmJbvc]
63
+
64
+ = Documentation =
65
+ Instructions for plugin usage are available on the plugin's [documentation page](http://www.wprssaggregator.com/documentation/).
66
+
67
+ = As featured on =
68
+ * [Latest WP](http://www.latestwp.com/2015/03/15/wp-rss-aggregator-plugin-review/)
69
+ * [WP Beginner](http://www.wpbeginner.com/plugins/how-to-fetch-feeds-in-wordpress-using-wp-rss-aggregator/)
70
+ * [WPEXplorer](http://www.wpexplorer.com/custom-rss-aggregator-plugin/)
71
+ * [WP Kube](http://www.wpkube.com/wp-rss-aggregator-wordpress-review/)
72
+ * [Torquemag](http://torquemag.io/wp-rss-aggregator-review-do-more-with-rss-feeds/)
73
+ * [MyWPExpert](http://www.mywpexpert.com/wordpress-rss-aggregator-plugin)
74
+ * [Kikolani](http://kikolani.com/create-latest-posts-portfolio-page-wp-rss-aggregator.html)
75
+ * [ManageWP Plugins of the Month](http://managewp.com/free-wordpress-plugins-march-2014)
76
+ * [TidyRepo](http://tidyrepo.com/wp-rss-aggregator/)
77
+ * [WP Eka](http://www.wpeka.com/wp-rss-aggregators-plugin.html)
78
+ * [IndexWP](www.indexwp.com/wp-rss-aggregator-plugin-review/)
79
+ * [WPulsar](http://www.wpulsar.com/wp-rss-aggregator-plugin-feed-to-posts-keyword-filtering-review/)
80
+ * [Kevin Muldoon](http://www.kevinmuldoon.com/wp-rss-aggregator-wordpress-plugin/)
81
+
82
+ = Translations =
83
+ * Italian - Davide De Maestri
84
+ * Spanish - Andrew Kurtis
85
+ * Brazilian Portugese - Bruno Calheira
86
+ * Dutch - Erick Suiker
87
+
88
+ == Installation ==
89
+
90
+ 1. Upload the `wp-rss-aggregator` folder to the `/wp-content/plugins/` directory
91
+ 2. Activate the WP RSS Aggregator plugin through the 'Plugins' menu in WordPress
92
+ 3. Configure the plugin by going to the `RSS Aggregator` menu item that appears in your dashboard menu.
93
+ 3. Use the shortcode in your posts or pages: `[wp-rss-aggregator]`
94
+
95
+ The parameters accepted are:
96
+
97
+ * links_before
98
+ * links_after
99
+ * link_before
100
+ * link_after
101
+ * limit
102
+ * source
103
+ * exclude
104
+ * pagination
105
+
106
+ An example of a shortcode with parameters:
107
+ `[wp_rss_aggregator link_before='<li class="feed-link">' link_after='</li>']`
108
+ It is advisable to use the 'HTML' view of the editor when inserting the shortcode with paramters.
109
+
110
+ For a full list of shortcode parameters and usage guide please refer to the [documentation](http://www.wprssaggregator.com/docs/shortcodes/).
111
+
112
+ __Usage within theme files__
113
+
114
+ An example of a function call from within the theme's files:
115
+ `
116
+ <?php
117
+ wprss_display_feed_items( $args = array(
118
+ 'links_before' => '<ul>',
119
+ 'links_after' => '</ul>',
120
+ 'link_before' => '<li>',
121
+ 'link_after' => '</li>',
122
+ 'limit' => '8',
123
+ 'source' => '5,9'
124
+ ));
125
+ ?>
126
+ `
127
+
128
+ OR
129
+
130
+ `<?php do_shortcode('[wp-rss-aggregator]'); ?>`
131
+
132
+
133
+ == Frequently Asked Questions ==
134
+ = How do I display the imported feed items? =
135
+
136
+ You can either use the shortcode in your posts and pages:
137
+ `[wp-rss-aggregator]`
138
+
139
+ or you can call the function directly within your theme:
140
+ `<?php wprss_display_feed_items(); ?>`
141
+
142
+ = Is there a limit on the number of feed sources I can use? =
143
+
144
+ There is no limit in place for the number of feed sources. Having many (50+) feed sources should not present any problems in itself.
145
+
146
+ However, pulling in posts from many sites is bound to put your server under some stress, so you might want to consider using a hosting solution that goes beyond your typical shared host.
147
+
148
+ Check out our dedicated page on [WordPress hosting](http://www.wprssaggregator.com/recommended-web-hosts/) recommendations.
149
+
150
+ = Does WP RSS Aggregator work using JSON as the source? =
151
+
152
+ No, our plugin does not currently import from JSON, it only imports from RSS and Atom structured XML.
153
+
154
+ = Why do I get “No feed items found” when I insert the shortcode on a page or post? =
155
+
156
+ Try adding a few more feed sources and make sure they are valid by using the RSS Feed validator.
157
+
158
+ Secondly make sure your WordPress cron system is working well. If not, the feeds cannot be imported. If in doubt you can go to RSS Aggregator > Debugging and hit the red button to re-import all feed items. If the problem persists contact support.
159
+
160
+ = Can I store imported feed items as posts? =
161
+
162
+ Yes! You can do that with the [Feed to Post](http://www.wprssaggregator.com/extensions/feed-to-post) add-on. You will not only be able to store items as posts, but also as other custom post types, as well as set the author, auto set tags and categories, import images into the gallery or set featured images, and much more.
163
+
164
+ = Some RSS feeds only give a short excerpt. Any way around that? =
165
+
166
+ Yes, along with the [Feed to Post](http://www.wprssaggregator.com/extensions/feed-to-post) add-on we have another add-on called [Full Text RSS Feeds](http://www.wprssaggregator.com/extension/full-text-rss-feeds/) that can get the full content of those feeds that only supply a short excerpt.
167
+
168
+ = I’m not sure which premium add-ons are right for me. Can you help me out? =
169
+
170
+ Sure! We wrote a [post](http://www.wprssaggregator.com/add-ons-purchase/) just for you. Read about which add-ons you should buy, we explain the different types of usage so you’ll know what to expect when purchasing.
171
+
172
+ If you need any further help you can contact our support team [here](http://www.wprssaggregator.com/contact/).
173
+
174
+ = Where can I find the documentation for the plugin? =
175
+
176
+ The full documentation section can be found on the [WP RSS Aggregator website](http://docs.wprssaggregator.com/), the documentation also includes an extensive FAQ list.
177
+
178
+
179
+ == Screenshots ==
180
+
181
+ 1. Feed items imported by WP RSS Aggregator displayed on the front-end using the shortcode.
182
+
183
+ 2. Feed Items imported by WP RSS Aggregator and displayed with the [Excerpts & Thumbnails](http://www.wprssaggregator.com/extensions/excerpts-thumbnails) add-on installed.
184
+
185
+ 3. Adding/Editing a feed source.
186
+
187
+ 4. The feed sources.
188
+
189
+ 5. The imported feeds items.
190
+
191
+ 6. WP RSS Aggregator's Settings page.
192
+
193
+
194
+ == Changelog ==
195
+
196
+ = 4.7.8 (2015-11-18) =
197
+ * Fixed bug: Sticky posts no longer get deleted when truncating, unless imported from a feed source.
198
+ * Enhanced: Added autoloading and refactored licensing.
199
+ * Enhanced: Added button to download error log.
200
+ * Enhanced: Cosmetic changes and fixes.
201
+
202
+ = 4.7.7 (2015-10-19) =
203
+ * Enhanced: Optimized checking for plugin updates.
204
+
205
+ = 4.7.6 (2015-10-07) =
206
+ * Enhanced: Feeds that fail to validate due to whitespace at the beginning are now supported by the plugin.
207
+ * Fixed bug: Undefined variables in the System Info section in the Debugging page.
208
+ * Fixed bug: Add-on license expiration notices could not be dismissed.
209
+
210
+ = 4.7.5 (2015-09-02) =
211
+ * Usage tracking now disabled.
212
+ * Fixed bug: error related to undefined `ajaxurl` JS variable gone from frontend.
213
+ * Enhanced: Licensing errors will be output to debug log.
214
+ * Enhanced: Improved compatibility with plugins that allow AJAX searching in the backend.
215
+
216
+ = 4.7.4 (2015-08-20) =
217
+ * Requirement: WordPress 4.0 or greater now required.
218
+ * Fixed bug in image caching
219
+ * Fixed bug in admin interface due to incorrectly translated IDs
220
+
221
+ = 4.7.3 (2015-08-04) =
222
+ * Enhanced: Core now implements an image cache logic.
223
+ * Enhanced: Add-ons on the "Add-ons" page now have an installed-but-inactive status.
224
+ * Enhanced: Google Alerts permalinks will now be normalized.
225
+ * Enhanced: Russian translation added.
226
+ * Fixed bug: Inline help (tooltips) translations now work.
227
+ * Fixed bug: Link to the Feed to Post add-on on the welcome page is no longer broken.
228
+
229
+ = 4.7.2 (2015-06-30) =
230
+ * Enhanced: Copyright updated.
231
+ * Fixed bug: Word trimming no longer adds extra closing tags at the end.
232
+ * Fixed bug: Presence of `idna_convert` class no longer causes infinite redirects on some servers.
233
+ * Fixed bug: Warning of unterminated comment no longer thrown in PHP 5.5.
234
+ * Fixed bug: Added default value for "Unique Titles" option.
235
+ * Fixed bug: Having a the port number specified with the database host no longer causes issues with the `mysqli` adapter in System Info on some servers.
236
+ * Fixed bug: Nested options of inline help controller no longer cause a fatal error.
237
+ * Fixed bug: Notices will no longer be displayed during rendering of feed items due to absence of required default values.
238
+
239
+ = 4.7.1 (2015-04-23) =
240
+ * Fixed bug: No warning will be thrown when fetching feeds.
241
+
242
+ = 4.7 (2015-04-21) =
243
+ * New: Optionally import only items with titles that don't already exist.
244
+ * Enhanced: Accessing feeds over HTTPS is now possible.
245
+ * Enhanced: Added support for multibyte strings in some places.
246
+ * Enhanced: Increased JS compatibility with other plugins.
247
+ * Enhanced: Increased UI support for mobile devices.
248
+ * Fixed bug: Having no mysqli extension no longer causes an error to appear in the debug info.
249
+ * Fixed bug: Saving an empty license key no longer results in a warning.
250
+
251
+ = 4.6.13 (2015-03-20) =
252
+ * Fixed bug: The "Force feed" option wasn't being correctly used.
253
+
254
+ = 4.6.12 (2015-03-09) =
255
+ * Fixed bug: The "Force feed" option was being removed by the Feed to Post add-on.
256
+
257
+ = 4.6.11 (2015-03-04) =
258
+ * Enhanced: The Help page now includes a support form if a premium add-on is detected.
259
+ * Enhanced: Updated some translations for admin options.
260
+ * Fixed bug: Help tooltips are now optimized for iPad screens.
261
+ * Fixed bug: Errors on the licensing page when a license code has not yet been entered.
262
+
263
+ = 4.6.10 (2015-02-10) =
264
+ * Enhanced: AJAX license activation.
265
+ * Enhanced: License form more reliable.
266
+ * Enhanced: license-related UI improvements
267
+ * New: Markdown library added. Changelog now read from readme.
268
+ * Fixed bug: Saving license keys not longer triggers error in some cases.
269
+
270
+ = 4.6.9 (2015-01-21) =
271
+ * Enhanced: Admin user will now be warned about invalid or expiring licenses.
272
+ * Enhanced: Admin notices logic centralized in this plugin.
273
+ * Fixed: Multiple small-scale security vulnerabilities.
274
+ * Fixed: Ampersand in feed URL no longer causes the product of generated feeds to be invalidated by W3C Validator.
275
+
276
+ = 4.6.8 (2015-01-07) =
277
+ * Enhanced: Added more logging during feed importing.
278
+ * Enhanced: Irrelevent metaboxes added by other plugins are now removed from the Add/Edit Feed Source page.
279
+ * Fixed bug: Valid feed URLS were being invalidated.
280
+ * Fixed bug: The Blacklist feature was being hidden when the Feed to Post add-on was enabled.
281
+ * Fixed bug: Patched a vulnerability where any user on the site can issue a feed fetch.
282
+ * Fixed bug: The "Activate" and "Pause" actions are not shown in the bulk actions dropdown in WordPress v4.1.
283
+
284
+ = 4.6.7 (2014-12-17) =
285
+ * Enhanced: Some minor interface updates.
286
+ * Enhanced: Added filters for use by the premium add-ons.
287
+
288
+ = 4.6.6 (2014-12-06) =
289
+ * Enhanced: Added output layouts for feed sources and feed items.
290
+ * Enhanced: Updated EDD updater class to version 1.5.
291
+ * Enhanced: Added time limit extending to prevent script from exhausting its execution time limit while importing.
292
+ * Fixed bug: The "Delete and Re-import" button was deleting items but not re-importing.
293
+ * Fixed bug: Non-object errors when a feed source is deleted while importing.
294
+
295
+ = 4.6.5 (2014-11-17) =
296
+ * Enhanced: Improved the logging.
297
+ * Enhanced: Improved the licensing fields.
298
+ * Enhanced: Updated the EDD updater class to the latest version.
299
+ * Fixed bug: Small random error when viewing the licenses page.
300
+
301
+ = 4.6.4 (2014-11-10) =
302
+ * Enhanced: Added filters to the custom feed.
303
+ * Enhanced: Updated some styles to improve the user interface.
304
+ * Fixed bug: The "Remove selected from Blacklist" button had no nonce associated with it.
305
+ * Fixed bug: The Blacklist menu entry was not always being shown.
306
+
307
+ = 4.6.3 (2014-11-3) =
308
+ Enhanced: Re-added the "Add New" link in the plugin's menu.
309
+ Enhanced: Improved error logging.
310
+ Enhanced: Bulk actions in the Feed Sources page are now also included in the bottom dropdown menu.
311
+ Fixed bug: Add-on updater was prone to conflicts. Now enclosed in an action.
312
+ Fixed bug: The Full Text RSS Feeds add-on was not showing as active in the "Add-ons" page.
313
+ Fixed bug: Broken links in the "Add-ons" page, to add-on pages on our site.
314
+
315
+ = 4.6.2 (2014-10-15) =
316
+ * Enhanced: Improved plugin responsiveness.
317
+ * Enhanced: Updated some help text in tooltips with better explainations and added clarity.
318
+ * Enhanced: Optimized some old SQL queries.
319
+ * Enhanced: Added better debug logging.
320
+ * Enhanced: Added a new filter to modify the text shown before author names.
321
+ * Fixed bug: Licenses were not showing as active, even though they were activated.
322
+
323
+ = 4.6.1 (2014-10-06) =
324
+ * Enhanced: Improved internationalization in the plugin, for better translations.
325
+ * Fixed bug: If the feed source age limit was left empty, the global setting was used instead of ignoring the limit.
326
+
327
+ = 4.6 (2014-09-22) =
328
+ * Enhanced: Improved the user interface, with better responsiveness and tooltips.
329
+ * Enhanced: Removes the ID column. The ID is now shown fixed in row actions.
330
+ * Enhanced: Feed Preview indicates if feed items have no dates.
331
+ * Fixed bug: If a feed item has no date, the date and time it was imported is used.
332
+
333
+ = 4.5.3 (2014-09-15) =
334
+ * New Featured: Added filter to allow adding RSS feeds to the head of your site's pages for CPTs.
335
+ * Enhanced: Columns in the feed sources table are now sortable.
336
+ * Enhanced: Removed the ID column in the feed sources table. The ID has been moved as a row action.
337
+ * Enhanced: Improved various interface elements.
338
+ * Enhanced: Better responsiveness for smaller screen.
339
+ * Fixed bug: The importing spinning icon would get stuck and spin for a very long time.
340
+ * Fixed bug: Removed an old description meta field.
341
+ * Fixed bug: Plugin was not removing all scheduled cron jobs when deactivated.
342
+
343
+ = 4.5.2 (2014-09-09) =
344
+ * Enhanced: Optimized plugin for WordPress 4.0.
345
+ * Enhanced: Improved template and added filters for add-on hooking.
346
+ * Fixed bug: Editor toolbar visible over the WP RSS shortcode dialog.
347
+
348
+ = 4.5.1 (2014-08-26) =
349
+ * Fixed bug: Last import feed item count stays at zero.
350
+ * Fixed bug: Datetime::setTimestamp error when using PHP 5.2 or earlier.
351
+ * Fixed bug: The display limit was not working.
352
+ * Fixed bug: Minor bug in licensing.
353
+
354
+ = 4.5 (2014-08-25) =
355
+ * New Feature: Bulk importer allows you to create multiple feed sources at once.
356
+ * Enhanced: Improved OPML importer with added hooks.
357
+ * Enhanced: Centralized add-on licensing, fixing multiple bugs.
358
+ * Fixed bug: Undefined `feed_limit` errors when using the shortcode.
359
+
360
+ = 4.4.4 (2014-08-19) =
361
+ * Fixed bug: Errors when using older PHP versions 5.3 or lower.
362
+
363
+ = 4.4.3 (2014-08-19) =
364
+ * Fixed bug: Errors when using older PHP versions 5.3 or lower.
365
+
366
+ = 4.4.2 (2014-08-19) =
367
+ * Fixed bug: Errors when using older PHP versions 5.3 or lower.
368
+
369
+ = 4.4.1 (2014-08-18) =
370
+ * Enhanced: Various improvements to the plugin interface and texts.
371
+ * Enhanced: Moved the restore default settings button farther down the Debugging page, to avoid confusion with the delete button.
372
+ * Fixed bug: Feed item dates were not being adjusted to the timezone when using a GMT offset.
373
+ * Fixed bug: Feed item dates are now adjusted according to daylight savings time.
374
+
375
+ = 4.4 (2014-08-11) =
376
+ * New Feature: Blacklist - delete items and blacklist them to never import them again.
377
+ * Enhanced: Added a button in the Debugging page to reset the plugin settings to default.
378
+ * Enhanced: WordPress Yoast SEO metaboxes and custom columns will no longer appear.
379
+
380
+ = 4.3.1 (2014-08-08) =
381
+ * Enhanced: Better wording on settings page.
382
+ * Fixed bug: The Links Behaviour option in the settings was not working.
383
+ * Fixed bug: The wrong feed items were being shown for some sources when using the "View Items" row action.
384
+
385
+ = 4.3 (2014-08-04) =
386
+ * New Feature: Feed items now also import authors.
387
+ * Enhanced: Custom feed is now in RSS 2.0 format.
388
+ * Enhanced: Improved the display template for feed items.
389
+ * Fixed bug: Custom feed was not working in Firefox.
390
+ * Fixed bug: Some feed items were showing items from another feed source.
391
+ * Fixed bug: The feed limit in the global settings was not working.
392
+
393
+ = 4.2.3 (2014-07-29) =
394
+ * Enhanced: Added an option to choose between the current pagination type, and numbered pagination.
395
+ * Enhanced: The Feed Preview now also shows the total number of items in the feed.
396
+ * Fixed bug: A PHP warning error was being shown in the System Info.
397
+ * Fixed bug: Language files were not always being referenced correctly.
398
+ * Fixed bug: Manually fetching a feed fails if the feed is scheduled to update in the next 10 minutes.
399
+ * Fixed bug: Bing RSS feeds were importing duplicates on every update.
400
+
401
+ = 4.2.2 (2014-07-23) =
402
+ * Enhanced: Facebook page feeds are now changed into RSS 2.0 feeds, rather than Atom 1.0 feeds.
403
+ * Enhanced: Improved live updating performace on the Feed Sources page.
404
+
405
+ = 4.2.1 (2014-07-17) =
406
+ * Enhanced: Feed Sources page is now more responsive.
407
+
408
+ = 4.2 (2014-07-17) =
409
+ * New Feature: Can now view each feed source's imported feed items separate from other feed sources' feed items.
410
+ * Enhanced: Major visual update to the Feed Sources page with new live updates.
411
+ * Enhanced: The custom feed now includes the feed source.
412
+ * Fixed bug: Google News feeds were importing duplicate items on every update.
413
+ * Fixed bug: Multiple minor bug fixes with old filters.
414
+
415
+ = 4.1.6 (2014-06-28) =
416
+ * Fixed bug: Results returned by wprss_get_feed_items_for_source() will no longer be affected by filters.
417
+ * Fixed bug: Charset issue in titles
418
+
419
+ = 4.1.5 (2014-06-19) =
420
+ * Enhanced: The Feed Sources table now indicates which feed sources encountered errors during the last import.
421
+ * Fixed bug: Feed titles were not being decoded for HTML entities.
422
+
423
+ = 4.1.4 (2014-05-16) =
424
+ * Enhanced: Minor improvements to feed importing and handling.
425
+ * Fixed bug: HTML entities were not being decoded in feed item titles.
426
+
427
+ = 4.1.3 (2014-04-28) =
428
+ * Enhanced: Added a force feed option, for valid RSS feeds with incorrect header content types.
429
+ * Fixed bug: HTML entities in feed item titles are now being decoded.
430
+
431
+ = 4.1.2 (2014-04-22) =
432
+ * Enhanced: Improved the custom feed, by allowing a custom title.
433
+ * Enhanced: Improved shortcode, by adding the "pagination" parameter.
434
+ * Enhanced: Modified a filter to fix some bugs in the add-ons.
435
+
436
+ = 4.1.1 (2014-04-09) =
437
+ * Enhanced: Tracking notices only appear for admin users.
438
+ * Fixed bug: Auto Feed Discovery was not working.
439
+
440
+ = 4.1 (2014-04-03) =
441
+ * New Feature: Feed items can now link to enclosure links in the feed.
442
+ * Enhanced: Added a filter to allow add-ons to modify feed item queries.
443
+
444
+ = 4.0.9 (2014-03-27) =
445
+ * Enhanced: Added a filter to modify the feeds template.
446
+ * Fixed bug: Nested lists in feeds template.
447
+
448
+ = 4.0.8 (2014-03-20) =
449
+ * Fixed bug: Using the shortcode makes the comments section always open.
450
+
451
+ = 4.0.7 (2014-03-08) =
452
+ * Fixed bug: The plugin prevented uploading of header images.
453
+
454
+ = 4.0.6 (2014-03-05) =
455
+ * Fixed bug: Hook change in last version suspected reason for some installations having non-updated feed items.
456
+
457
+ = 4.0.5 (2014-03-03) =
458
+ * New Feature: Time ago added as an option.
459
+ * Enhanced: The plugin now allows the use of RSS and Atom feeds that do not specify the correct MIME type.
460
+ * Enhanced: Better performance due to better hook usage.
461
+ * Fixed bug: Facebook page feed URL conversion was not being triggered for new feed sources.
462
+ * Fixed bug: Styles fix for pagination.
463
+ * Fixed bug: Removed empty spaces in logging.
464
+
465
+ = 4.0.4 (2014-02-17) =
466
+ * Enhanced: Added Activate/Pause bulk actions in the Feed Sources page.
467
+ * Enhanced: Feed Sources page table has been re-designed.
468
+ * Enhanced: Logging is now site dependant on multisite.
469
+ * Fixed bug: Undefined display settings where appearing on the front end.
470
+
471
+ = 4.0.3 (2014-02-12) =
472
+ * Fixed bug: The general setting for deleting feed items by age was not working.
473
+
474
+ = 4.0.2 (2014-02-10) =
475
+ * Enhanced: Added a filter to change the html tags allowed in feed item content.
476
+
477
+ = 4.0.1 (2014-02-08) =
478
+ * Fixed bug: Empty array of feed items bug caused importing problems.
479
+
480
+ = 4.0 (2014-02-04) =
481
+ * Enhanced: Improved some internal queries, for better performance.
482
+ * Fixed bug: Feed limits were not working properly.
483
+
484
+ = 3.9.9 (2014-02-03) =
485
+ * Enhanced: The custom feed can now be extended by add-ons.
486
+
487
+ = 3.9.8 (2014-01-20) =
488
+ * Fixed bug: Removed excessive logging from Debugging Error Log.
489
+
490
+ = 3.9.7 (2014-01-17) =
491
+ * Fixed bug: Bug in admin-debugging.php causing trouble with admin login
492
+
493
+ = 3.9.6 (2014-01-17) =
494
+ * Enhanced: Added error logging.
495
+
496
+ = 3.9.5 (2014-01-02) =
497
+ * Enhanced: Added a feed validator link in the New/Edit Feed Sources page.
498
+ * Enhanced: The Next Update column also shows the time remaining for next update, for feed source on the global update interval.
499
+ * Enhanced: The custom feed has been improved, and is now identical to the feeds displayed with the shortcode.
500
+ * Enhanced: License notifications only appear on the main site when using WordPress multisite.
501
+ * Enhanced: Updated Colorbox script to 1.4.33
502
+ * Fixed bug: The Imported Items column was always showing zero.
503
+ * Fixed bug: Feed items not being imported with limit set to zero. Should be unlimited.
504
+ * Fixed bug: Fat header in Feed Sources page
505
+
506
+ = 3.9.4 (2013-12-24) =
507
+ * Enhanced: Added a column in the Feed Sources page that shows the number of feed items imported for each feed source.
508
+ * Fixed bug: Leaving the delete old feed items empty did not ignore the delete.
509
+
510
+ = 3.9.3 (2013-12-23) =
511
+ * Fixed bug: Fixed tracking pointer appearing on saving settings.
512
+
513
+ = 3.9.2 (2013-12-21) =
514
+ * Fixed bug: Incorrect file include call.
515
+
516
+ = 3.9.1 (2013-12-12) =
517
+ * Enhanced: Improved date and time handling for imported feed items.
518
+ * Fixed bug: Incorrect values being shown in the Feed Processing metabox.
519
+ * Fixed bug: Feed limits set to zero were causing feeds to not be imported.
520
+
521
+ = 3.9 (2013-12-12) =
522
+ * New Feature: Feed sources can have their own update interval.
523
+ * New Feature: The time remaining until the next update has been added to the Feed Source table.
524
+
525
+ = 3.8 (2013-12-05) =
526
+ * New Feature: Feed items can be limited and deleted by their age.
527
+ * Enhanced: Added utility functions for shorter filters.
528
+ * Fixed bug: License codes were being erased when add-ons were deactivated.
529
+ * Fixed bug: Some feed sources could not be set to active from the table controls.
530
+ * Fixed bug: str_pos errors appear when custom feed url is left empty.
531
+ * Fixed bug: Some options were producing undefined index errors.
532
+
533
+ = 3.7 (2013-11-28) =
534
+ * New Feature: State system - Feed sources can be activated/paused.
535
+ * New Feature: State system - Feed sources can be set to activate or pause themselves at a specific date and time.
536
+ * Enhanced: Added compatibility with nested outline elements in OPML files.
537
+ * Enhanced: Admin menu icon image will change into a Dashicon, when WordPress is updated to 3.8 (Decemeber 2013).
538
+ * Fixed bug: Custom Post types were breaking when the plugin is activated.
539
+
540
+ = 3.6.1 (2013-11-17) =
541
+ * Fixed bug: Missing 2nd argument for wprss_shorten_title()
542
+
543
+ = 3.6 (2013-11-16) =
544
+ * New Feature: Can set the maximum length for titles. Long titles get trimmed.
545
+ * Fixed bug: Fixed errors with undefined indexes for unchecked checkboxes in the settings page.
546
+ * Fixed bug: Pagination on front static page was not working.
547
+
548
+ = 3.5.2 (2013-11-11) =
549
+ * Fixed bug: Invalid feed source url was producing an Undefined method notice.
550
+ * Fixed bug: Custom feed was producing a 404 page.
551
+ * Fixed bug: Presstrends code firing on admin_init, opt-in implementation coming soon
552
+
553
+ = 3.5.1 (2013-11-09) =
554
+ * Enhanced: Increased compatibility with RSS sources.
555
+ * Fixed bug: Pagination not working on home page
556
+
557
+ = 3.5 (2013-11-6) =
558
+ * New Feature: Can delete feed items for a particular source
559
+ * Enhanced: the 'Fetch feed items' row action for feed sources resets itself after 3.5 seconds.
560
+ * Enhanced: The feed image is saved for each url.
561
+ * Fixed bug: Link to source now links to correct url. Previously linked to site's feed.
562
+
563
+ = 3.4.6 (2013-11-1) =
564
+ * Enhanced: Added more hooks to debugging page for the Feed to Post add-on.
565
+ * Fixed bug: Uninitialized loop index
566
+
567
+ = 3.4.5 (2013-10-30) =
568
+ * Bug Fix: Feed items were not being imported while the WPML plugin was active.
569
+
570
+ = 3.4.4 (2013-10-26) =
571
+ * New feature: Pagination
572
+ * New feature: First implementation of editor button for easy shortcode creation
573
+ * Enhanced: Feed items and sources don't show up in link manager
574
+ * Enhanced: Included Presstrends code for plugin usage monitoring
575
+
576
+ = 3.4.3 (2013-10-20) =
577
+ * Fixed bug: Removed anonymous functions for backwards PHP compatibility
578
+ * Bug fix: Added suppress_filters in feed-display.php to prevent a user reported error
579
+ * Bug fix: Missing <li> in certain feed displays
580
+
581
+ = 3.4.2 (2013-9-19) =
582
+ * Enhanced: Added some hooks for Feed to Post compatibility
583
+ * Enhanced: Moved date settings to a more appropriate location
584
+
585
+ = 3.4.1 (2013-9-16) =
586
+ * Fixed Bug: Minor issue with options page - PHP notice
587
+
588
+ = 3.4 (2013-9-15) =
589
+ * New Feature: Saving/Updating a feed source triggers an update for that source's feed items.
590
+ * New Feature: Option to change Youtube, Vimeo and Dailymotion feed item URLs to embedded video players URLs
591
+ * New Feature: Facebook Pages URLs are automatically detected and changed into Atom Feed URLs using FB's Graph
592
+ * Enhanced: Updated jQuery Colorbox library to 1.4.29
593
+ * Fixed Bug: Some settings did not have a default value set, and were throwing an 'Undefined Index' error
594
+ * Fixed Bug: Admin notices do not disappear immediately when dismissed.
595
+
596
+ = Version 3.3.3 (2013-09-08) =
597
+ * Fixed bug: Better function handling on uninstall, should remove uninstall issues
598
+
599
+ = Version 3.3.2 (2013-09-07) =
600
+ * New feature: Added exclude parameter to shortcode
601
+ * Enhanced: Added metabox links to documentation and add-ons
602
+ * Fixed bug: Custom feed linking to post on user site rather than original source
603
+ * Fixed bug: Custom post types issues when activitating the plugin
604
+
605
+ = Version 3.3.1 (2013-08-09) =
606
+ * Fixed Bug: Roles and Capabilities file had not been included
607
+ * Fixed Bug: Error on install, function not found
608
+
609
+ = Version 3.3 (2013-08-08) =
610
+ * New feature: OPML importer
611
+ * New feature: Feed item limits for individual Feed Sources
612
+ * New feature: Custom feed URL
613
+ * New feature: Feed limit on custom feed
614
+ * New feature: New 'Fetch feed items' action for each Feed Source in listing display
615
+ * New feature: Option to enable link to source
616
+ * Enhanced: Date strings now change according to locale being used (i.e. compatible with WPML)
617
+ * Enhanced: Capabilities implemented
618
+ * Enhanced: Feed Sources row action 'View' removed
619
+ * Fixed Bug: Proxy feed URLs resulting in the permalink: example.com/url
620
+
621
+ = Version 3.2 (2013-07-06) =
622
+ * New feature: Parameter to limit number of feeds displayed
623
+ * New feature: Paramter to limit feeds displayed to particular sources (via ID)
624
+ * Enhanced: Better feed import handling to handle large number of feed sources
625
+
626
+ = Version 3.1.1 (2013-06-06) =
627
+ * Fixed bug: Incompatibility with some other plugins due to function missing namespace
628
+
629
+ = Version 3.1 (2013-06-06) =
630
+ * New feature: Option to set the number of feed items imported from every feed (default 5)
631
+ * New feature: Import and Export aggregator settings and feed sources
632
+ * New feature: Debugging page allowing manual feed refresh and feed reset
633
+ * Enhanced: Faster handling of restoring sources from trash when feed limit is 0
634
+ * Fixed bug: Limiter on number of overall feeds stored not working
635
+ * Fixed bug: Incompatibility issue with Foobox plugin fixed
636
+ * Fixed bug: Duplicate feeds sometimes imported
637
+
638
+ = Version 3.0 (2013-03-16) =
639
+ * New feature: Option to select cron frequency
640
+ * New feature: Code extensibility added to be compatible with add-ons
641
+ * New feature: Option to set a limit to the number of feeds stored (previously 50, hard coded)
642
+ * New feature: Option to define the format of the date shown below each feed item
643
+ * New feature: Option to show or hide source of feed item
644
+ * New feature: Option to show or hide publish date of feed item
645
+ * New feature: Option to set text preceding publish date
646
+ * New feature: Option to set text preceding source of feed item
647
+ * New feature: Option to link title or not
648
+ * New feature: Limit of 5 items imported for each source instead of 10
649
+ * Enhanced: Performance improvement when publishing * New feeds in admin
650
+ * Enhanced: Query tuning for better performance
651
+ * Enhanced: Major code rewrite, refactoring and inclusion of hooks
652
+ * Enhanced: Updated Colorbox to v1.4.1
653
+ * Enhanced: Better security implementations
654
+ * Enhanced: Better feed preview display
655
+ * Fixed bug: Deletion of items upon source deletion not working properly
656
+ * Requires: WordPress 3.3
657
+
658
+ = Version 2.2.3 (2012-11-01) =
659
+ * Fixed bug: Tab navigation preventing typing in input boxes
660
+ * Removed: Feeds showing up in internal linking pop up
661
+
662
+ = Version 2.2.2 (2012-10-30) =
663
+ * Removed: Feeds showing up in site search results
664
+ * Enhanced: Better tab button navigation when adding a new feed
665
+ * Enhanced: Better guidance when a feed URL is invalid
666
+
667
+ = Version 2.2.1 (2012-10-17) =
668
+ * Fixed bug: wprss_feed_source_order assumes everyone is an admin
669
+
670
+ = Version 2.2 (2012-10-01) =
671
+ * Italian translation added
672
+ * Feed source order changed to alphabetical
673
+ * Fixed bug - repeated entries when having a non-valid feed source
674
+ * Fixed bug - all imported feeds deleted upon trashing a single feed source
675
+
676
+ = Version 2.1 (2012-09-27) =
677
+ * Now localised for translations
678
+ * Fixed bug with date string
679
+ * Fixed $link_before and $link_after, now working
680
+ * Added backwards compatibility for wp_rss_aggregator() function
681
+
682
+ = Version 2.0 (2012-09-21) =
683
+ * Bulk of code rewritten and refactored
684
+ * Added install and upgrade functions
685
+ * Added DB version setting
686
+ * Feed sources now stored as Custom Post Types
687
+ * Feed source list sortable ascending or descending by name
688
+ * Removed days subsections in feed display
689
+ * Ability to limit total number of feeds displayed
690
+ * Feeds now fetched via Cron
691
+ * Cron job to delete old feed items, keeps max of 50 items in DB
692
+ * Now requires WordPress 3.2
693
+ * Updated colorbox to v1.3.20.1
694
+ * Limit of 15 items max imported for each source
695
+ * Fixed issue of page content displaying incorrectly after feeds
696
+
697
+ = Version 1.1 (2012-08-13) =
698
+ * Now requires WordPress 3.0
699
+ * More flexible fetching of images directory
700
+ * Has its own top level menu item
701
+ * Added settings section
702
+ * Ability to open in lightbox, new window or default browser behaviour
703
+ * Ability to set links as follow or no follow
704
+ * Using constants for oftenly used locations
705
+ * Code refactoring
706
+ * Changes in file and folder structure
707
+
708
+ = Version 1.0 (2012-01-06) =
709
+ === WP RSS Aggregator ===
710
+ Contributors: jeangalea, Mekku, xedin.unknown, markzahra, doytch, chiragswadia
711
+ Plugin URI: http://www.wprssaggregator.com
712
+ Tags: rss, aggregation, autoblog, autoblog aggregator, autoblogger, autoblogging, autopost, content curation, feed aggregation, feed aggregator, feed import, feed reader, feed to post, feeds, multi feed import, multi feed importer, multi rss feeds, multiple feed import, multiple rss feeds,rss aggregator, rss feader, RSS Feed, rss feed to post, rss feeder, RSS import, rss multi importer, rss post importer, rss retriever, rss to post, syndication
713
+ Requires at least: 4.0
714
+ Tested up to: 4.3.1
715
+ Stable tag: 4.7.7
716
+ License: GPLv2 or later
717
+ The no.1 RSS feed importer for WordPress. Premium add-ons available for more functionality.
718
+
719
+
720
+ == Description ==
721
+
722
+ WP RSS Aggregator is the most comprehensive and elegant RSS feed solution for WordPress.
723
+
724
+ The original and premier plugin for importing, merging and displaying RSS and Atom feeds on your WordPress site.
725
+
726
+ With WP RSS Aggregator, you can:
727
+
728
+ * Display feeds from one or more sites on your blog
729
+ * Aggregate feeds from multiple sites
730
+
731
+ You can add any number of feeds through an administration panel, the plugin will then pull feed items from these sites, merge them and display them in date order.
732
+
733
+ To [display your imported feed items](http://wordpress.org/plugins/wp-rss-aggregator/screenshots/), you can use a shortcode or call the display function directly from within your theme.
734
+
735
+ = Highlighted Features =
736
+
737
+ * Export a custom RSS feed based on your feed sources
738
+ * Pagination
739
+ * Set the feed import time interval
740
+ * Scheduling of feed imports by feed source
741
+ * Various shortcode parameters you can use to further customize the output
742
+ * Choose whether to show/hide sources and dates
743
+ * Choose the date format
744
+ * Set the links as no-follow or not, or add no follow to meta tag
745
+ * Select how you would like the links to open (in a Lightbox, a new window, or the current window)
746
+ * Set the name of the feed source
747
+ * Select number of posts per feed you want to show and store
748
+ * Opens YouTube, DailyMotion and Vimeo videos directly
749
+ * Limit number of feed items stored in the database
750
+ * Feed autodiscovery, which lets you add feeds without even knowing the exact URL.
751
+ * Extendable via action and filter hooks
752
+ * Integrated with the Simplepie library that come with WordPress. This includes RSS 0.91 and RSS 1.0 formats, the popular RSS 2.0 format, Atom etc.
753
+
754
+ = Premium Add-Ons =
755
+ Add-Ons that add more functionality to the core plugin are [available for purchase](http://www.wprssaggregator.com/extensions/).
756
+
757
+ * [Feed to Post](http://www.wprssaggregator.com/extensions/feed-to-post) - an advanced importer that lets you import RSS to posts or custom post types. Populate a website in minutes (autoblog). This is the most popular extension.
758
+ * [Keyword Filtering](http://www.wprssaggregator.com/extensions/keyword-filtering) - filter imported feeds based on keywords, so you only get items you're interested in.
759
+ * [Excerpts & Thumbnails](http://www.wprssaggregator.com/extensions/excerpts-thumbnails) - display excerpts and thumbnails together with the title, date and source.
760
+ * [Categories](http://www.wprssaggregator.com/extensions/categories) - categorise your feed sources and display items from a particular category at will within your site.
761
+ * [WordAi](http://www.wprssaggregator.com/extension/wordai/) - WordAi allows users to take an RSS feed and turn it into new content that is both completely unique and completely readable.
762
+ * [Full Text RSS Feeds](http://www.wprssaggregator.com/extension/full-text-rss-feeds/) - connectivity to our Full Text Premium service, which gives you unlimited feed items returned per feed source.
763
+ * [Widget](http://www.wprssaggregator.com/extension/widget/) - Add a widget that displays imported feed items.
764
+
765
+ We also provide a [Feed Creator](http://createfeed.wprssaggregator.com) service, that allows you to generate RSS feeds from any webpage, even if it doesn't have its own RSS feed.
766
+
767
+ = Demo =
768
+ The core plugin can be seen in use on the [demo page](http://www.wprssaggregator.com/demo/).
769
+
770
+ = Video Walkthrough =
771
+ [youtube http://www.youtube.com/watch?v=fcENPsmJbvc]
772
+
773
+ = Documentation =
774
+ Instructions for plugin usage are available on the plugin's [documentation page](http://www.wprssaggregator.com/documentation/).
775
+
776
+ = As featured on =
777
+ * [Latest WP](http://www.latestwp.com/2015/03/15/wp-rss-aggregator-plugin-review/)
778
+ * [WP Beginner](http://www.wpbeginner.com/plugins/how-to-fetch-feeds-in-wordpress-using-wp-rss-aggregator/)
779
+ * [WPEXplorer](http://www.wpexplorer.com/custom-rss-aggregator-plugin/)
780
+ * [WP Kube](http://www.wpkube.com/wp-rss-aggregator-wordpress-review/)
781
+ * [Torquemag](http://torquemag.io/wp-rss-aggregator-review-do-more-with-rss-feeds/)
782
+ * [MyWPExpert](http://www.mywpexpert.com/wordpress-rss-aggregator-plugin)
783
+ * [Kikolani](http://kikolani.com/create-latest-posts-portfolio-page-wp-rss-aggregator.html)
784
+ * [ManageWP Plugins of the Month](http://managewp.com/free-wordpress-plugins-march-2014)
785
+ * [TidyRepo](http://tidyrepo.com/wp-rss-aggregator/)
786
+ * [WP Eka](http://www.wpeka.com/wp-rss-aggregators-plugin.html)
787
+ * [IndexWP](www.indexwp.com/wp-rss-aggregator-plugin-review/)
788
+ * [WPulsar](http://www.wpulsar.com/wp-rss-aggregator-plugin-feed-to-posts-keyword-filtering-review/)
789
+ * [Kevin Muldoon](http://www.kevinmuldoon.com/wp-rss-aggregator-wordpress-plugin/)
790
+
791
+ = Translations =
792
+ * Italian - Davide De Maestri
793
+ * Spanish - Andrew Kurtis
794
+ * Brazilian Portugese - Bruno Calheira
795
+ * Dutch - Erick Suiker
796
+
797
+ == Installation ==
798
+
799
+ 1. Upload the `wp-rss-aggregator` folder to the `/wp-content/plugins/` directory
800
+ 2. Activate the WP RSS Aggregator plugin through the 'Plugins' menu in WordPress
801
+ 3. Configure the plugin by going to the `RSS Aggregator` menu item that appears in your dashboard menu.
802
+ 3. Use the shortcode in your posts or pages: `[wp-rss-aggregator]`
803
+
804
+ The parameters accepted are:
805
+
806
+ * links_before
807
+ * links_after
808
+ * link_before
809
+ * link_after
810
+ * limit
811
+ * source
812
+ * exclude
813
+ * pagination
814
+
815
+ An example of a shortcode with parameters:
816
+ `[wp_rss_aggregator link_before='<li class="feed-link">' link_after='</li>']`
817
+ It is advisable to use the 'HTML' view of the editor when inserting the shortcode with paramters.
818
+
819
+ For a full list of shortcode parameters and usage guide please refer to the [documentation](http://www.wprssaggregator.com/docs/shortcodes/).
820
+
821
+ __Usage within theme files__
822
+
823
+ An example of a function call from within the theme's files:
824
+ `
825
+ <?php
826
+ wprss_display_feed_items( $args = array(
827
+ 'links_before' => '<ul>',
828
+ 'links_after' => '</ul>',
829
+ 'link_before' => '<li>',
830
+ 'link_after' => '</li>',
831
+ 'limit' => '8',
832
+ 'source' => '5,9'
833
+ ));
834
+ ?>
835
+ `
836
+
837
+ OR
838
+
839
+ `<?php do_shortcode('[wp-rss-aggregator]'); ?>`
840
+
841
+
842
+ == Frequently Asked Questions ==
843
+ = How do I display the imported feed items? =
844
+
845
+ You can either use the shortcode in your posts and pages:
846
+ `[wp-rss-aggregator]`
847
+
848
+ or you can call the function directly within your theme:
849
+ `<?php wprss_display_feed_items(); ?>`
850
+
851
+ = Is there a limit on the number of feed sources I can use? =
852
+
853
+ There is no limit in place for the number of feed sources. Having many (50+) feed sources should not present any problems in itself.
854
+
855
+ However, pulling in posts from many sites is bound to put your server under some stress, so you might want to consider using a hosting solution that goes beyond your typical shared host.
856
+
857
+ Check out our dedicated page for hosting recommendations.
858
+
859
+ = Does WP RSS Aggregator work using JSON as the source? =
860
+
861
+ No, our plugin does not currently import from JSON, it only imports from RSS and Atom structured XML.
862
+
863
+ = Why do I get “No feed items found” when I insert the shortcode on a page or post? =
864
+
865
+ Try adding a few more feed sources and make sure they are valid by using the RSS Feed validator.
866
+
867
+ Secondly make sure your WordPress cron system is working well. If not, the feeds cannot be imported. If in doubt you can go to RSS Aggregator > Debugging and hit the red button to re-import all feed items. If the problem persists contact support.
868
+
869
+ = Can I store imported feed items as posts? =
870
+
871
+ Yes! You can do that with the [Feed to Post](http://www.wprssaggregator.com/extensions/feed-to-post) add-on. You will not only be able to store items as posts, but also as other custom post types, as well as set the author, auto set tags and categories, import images into the gallery or set featured images, and much more.
872
+
873
+ = Some RSS feeds only give a short excerpt. Any way around that? =
874
+
875
+ Yes, along with the [Feed to Post](http://www.wprssaggregator.com/extensions/feed-to-post) add-on we have another add-on called [Full Text RSS Feeds](http://www.wprssaggregator.com/extension/full-text-rss-feeds/) that can get the full content of those feeds that only supply a short excerpt.
876
+
877
+ = I’m not sure which premium add-ons are right for me. Can you help me out? =
878
+
879
+ Sure! We wrote a post just for you. Read about which add-ons you should buy, we explain the different types of usage so you’ll know what to expect when purchasing.
880
+
881
+ If you need any further help you can contact our support team [here](http://www.wprssaggregator.com/contact/).
882
+
883
+ = Where can I find the documentation for the plugin? =
884
+
885
+ The full documentation section can be found on the [WP RSS Aggregator website](www.wprssaggregator.com/documentation/), the documentation also includes an extensive FAQ list.
886
+
887
+
888
+ == Screenshots ==
889
+
890
+ 1. Feed items imported by WP RSS Aggregator displayed on the front-end using the shortcode.
891
+
892
+ 2. Feed Items imported by WP RSS Aggregator and displayed with the [Excerpts & Thumbnails](http://www.wprssaggregator.com/extensions/excerpts-thumbnails) add-on installed.
893
+
894
+ 3. Adding/Editing a feed source.
895
+
896
+ 4. The feed sources.
897
+
898
+ 5. The imported feeds items.
899
+
900
+ 6. WP RSS Aggregator's Settings page.
901
+
902
+
903
+ == Changelog ==
904
+
905
+ = 4.7.7 (2015-10-19) =
906
+ * Enhanced: Optimized checking for plugin updates.
907
+
908
+ = 4.7.6 (2015-10-07) =
909
+ * Enhanced: Feeds that fail to validate due to whitespace at the beginning are now supported by the plugin.
910
+ * Fixed bug: Undefined variables in the System Info section in the Debugging page.
911
+ * Fixed bug: Add-on license expiration notices could not be dismissed.
912
+
913
+ = 4.7.5 (2015-09-02) =
914
+ * Usage tracking now disabled.
915
+ * Fixed bug: error related to undefined `ajaxurl` JS variable gone from frontend.
916
+ * Enhanced: Licensing errors will be output to debug log.
917
+ * Enhanced: Improved compatibility with plugins that allow AJAX searching in the backend.
918
+
919
+ = 4.7.4 (2015-08-20) =
920
+ * Requirement: WordPress 4.0 or greater now required.
921
+ * Fixed bug in image caching
922
+ * Fixed bug in admin interface due to incorrectly translated IDs
923
+
924
+ = 4.7.3 (2015-08-04) =
925
+ * Enhanced: Core now implements an image cache logic.
926
+ * Enhanced: Add-ons on the "Add-ons" page now have an installed-but-inactive status.
927
+ * Enhanced: Google Alerts permalinks will now be normalized.
928
+ * Enhanced: Russian translation added.
929
+ * Fixed bug: Inline help (tooltips) translations now work.
930
+ * Fixed bug: Link to the Feed to Post add-on on the welcome page is no longer broken.
931
+
932
+ = 4.7.2 (2015-06-30) =
933
+ * Enhanced: Copyright updated.
934
+ * Fixed bug: Word trimming no longer adds extra closing tags at the end.
935
+ * Fixed bug: Presence of `idna_convert` class no longer causes infinite redirects on some servers.
936
+ * Fixed bug: Warning of unterminated comment no longer thrown in PHP 5.5.
937
+ * Fixed bug: Added default value for "Unique Titles" option.
938
+ * Fixed bug: Having a the port number specified with the database host no longer causes issues with the `mysqli` adapter in System Info on some servers.
939
+ * Fixed bug: Nested options of inline help controller no longer cause a fatal error.
940
+ * Fixed bug: Notices will no longer be displayed during rendering of feed items due to absence of required default values.
941
+
942
+ = 4.7.1 (2015-04-23) =
943
+ * Fixed bug: No warning will be thrown when fetching feeds.
944
+
945
+ = 4.7 (2015-04-21) =
946
+ * New: Optionally import only items with titles that don't already exist.
947
+ * Enhanced: Accessing feeds over HTTPS is now possible.
948
+ * Enhanced: Added support for multibyte strings in some places.
949
+ * Enhanced: Increased JS compatibility with other plugins.
950
+ * Enhanced: Increased UI support for mobile devices.
951
+ * Fixed bug: Having no mysqli extension no longer causes an error to appear in the debug info.
952
+ * Fixed bug: Saving an empty license key no longer results in a warning.
953
+
954
+ = 4.6.13 (2015-03-20) =
955
+ * Fixed bug: The "Force feed" option wasn't being correctly used.
956
+
957
+ = 4.6.12 (2015-03-09) =
958
+ * Fixed bug: The "Force feed" option was being removed by the Feed to Post add-on.
959
+
960
+ = 4.6.11 (2015-03-04) =
961
+ * Enhanced: The Help page now includes a support form if a premium add-on is detected.
962
+ * Enhanced: Updated some translations for admin options.
963
+ * Fixed bug: Help tooltips are now optimized for iPad screens.
964
+ * Fixed bug: Errors on the licensing page when a license code has not yet been entered.
965
+
966
+ = 4.6.10 (2015-02-10) =
967
+ * Enhanced: AJAX license activation.
968
+ * Enhanced: License form more reliable.
969
+ * Enhanced: license-related UI improvements
970
+ * New: Markdown library added. Changelog now read from readme.
971
+ * Fixed bug: Saving license keys not longer triggers error in some cases.
972
+
973
+ = 4.6.9 (2015-01-21) =
974
+ * Enhanced: Admin user will now be warned about invalid or expiring licenses.
975
+ * Enhanced: Admin notices logic centralized in this plugin.
976
+ * Fixed: Multiple small-scale security vulnerabilities.
977
+ * Fixed: Ampersand in feed URL no longer causes the product of generated feeds to be invalidated by W3C Validator.
978
+
979
+ = 4.6.8 (2015-01-07) =
980
+ * Enhanced: Added more logging during feed importing.
981
+ * Enhanced: Irrelevent metaboxes added by other plugins are now removed from the Add/Edit Feed Source page.
982
+ * Fixed bug: Valid feed URLS were being invalidated.
983
+ * Fixed bug: The Blacklist feature was being hidden when the Feed to Post add-on was enabled.
984
+ * Fixed bug: Patched a vulnerability where any user on the site can issue a feed fetch.
985
+ * Fixed bug: The "Activate" and "Pause" actions are not shown in the bulk actions dropdown in WordPress v4.1.
986
+
987
+ = 4.6.7 (2014-12-17) =
988
+ * Enhanced: Some minor interface updates.
989
+ * Enhanced: Added filters for use by the premium add-ons.
990
+
991
+ = 4.6.6 (2014-12-06) =
992
+ * Enhanced: Added output layouts for feed sources and feed items.
993
+ * Enhanced: Updated EDD updater class to version 1.5.
994
+ * Enhanced: Added time limit extending to prevent script from exhausting its execution time limit while importing.
995
+ * Fixed bug: The "Delete and Re-import" button was deleting items but not re-importing.
996
+ * Fixed bug: Non-object errors when a feed source is deleted while importing.
997
+
998
+ = 4.6.5 (2014-11-17) =
999
+ * Enhanced: Improved the logging.
1000
+ * Enhanced: Improved the licensing fields.
1001
+ * Enhanced: Updated the EDD updater class to the latest version.
1002
+ * Fixed bug: Small random error when viewing the licenses page.
1003
+
1004
+ = 4.6.4 (2014-11-10) =
1005
+ * Enhanced: Added filters to the custom feed.
1006
+ * Enhanced: Updated some styles to improve the user interface.
1007
+ * Fixed bug: The "Remove selected from Blacklist" button had no nonce associated with it.
1008
+ * Fixed bug: The Blacklist menu entry was not always being shown.
1009
+
1010
+ = 4.6.3 (2014-11-3) =
1011
+ Enhanced: Re-added the "Add New" link in the plugin's menu.
1012
+ Enhanced: Improved error logging.
1013
+ Enhanced: Bulk actions in the Feed Sources page are now also included in the bottom dropdown menu.
1014
+ Fixed bug: Add-on updater was prone to conflicts. Now enclosed in an action.
1015
+ Fixed bug: The Full Text RSS Feeds add-on was not showing as active in the "Add-ons" page.
1016
+ Fixed bug: Broken links in the "Add-ons" page, to add-on pages on our site.
1017
+
1018
+ = 4.6.2 (2014-10-15) =
1019
+ * Enhanced: Improved plugin responsiveness.
1020
+ * Enhanced: Updated some help text in tooltips with better explainations and added clarity.
1021
+ * Enhanced: Optimized some old SQL queries.
1022
+ * Enhanced: Added better debug logging.
1023
+ * Enhanced: Added a new filter to modify the text shown before author names.
1024
+ * Fixed bug: Licenses were not showing as active, even though they were activated.
1025
+
1026
+ = 4.6.1 (2014-10-06) =
1027
+ * Enhanced: Improved internationalization in the plugin, for better translations.
1028
+ * Fixed bug: If the feed source age limit was left empty, the global setting was used instead of ignoring the limit.
1029
+
1030
+ = 4.6 (2014-09-22) =
1031
+ * Enhanced: Improved the user interface, with better responsiveness and tooltips.
1032
+ * Enhanced: Removes the ID column. The ID is now shown fixed in row actions.
1033
+ * Enhanced: Feed Preview indicates if feed items have no dates.
1034
+ * Fixed bug: If a feed item has no date, the date and time it was imported is used.
1035
+
1036
+ = 4.5.3 (2014-09-15) =
1037
+ * New Featured: Added filter to allow adding RSS feeds to the head of your site's pages for CPTs.
1038
+ * Enhanced: Columns in the feed sources table are now sortable.
1039
+ * Enhanced: Removed the ID column in the feed sources table. The ID has been moved as a row action.
1040
+ * Enhanced: Improved various interface elements.
1041
+ * Enhanced: Better responsiveness for smaller screen.
1042
+ * Fixed bug: The importing spinning icon would get stuck and spin for a very long time.
1043
+ * Fixed bug: Removed an old description meta field.
1044
+ * Fixed bug: Plugin was not removing all scheduled cron jobs when deactivated.
1045
+
1046
+ = 4.5.2 (2014-09-09) =
1047
+ * Enhanced: Optimized plugin for WordPress 4.0.
1048
+ * Enhanced: Improved template and added filters for add-on hooking.
1049
+ * Fixed bug: Editor toolbar visible over the WP RSS shortcode dialog.
1050
+
1051
+ = 4.5.1 (2014-08-26) =
1052
+ * Fixed bug: Last import feed item count stays at zero.
1053
+ * Fixed bug: Datetime::setTimestamp error when using PHP 5.2 or earlier.
1054
+ * Fixed bug: The display limit was not working.
1055
+ * Fixed bug: Minor bug in licensing.
1056
+
1057
+ = 4.5 (2014-08-25) =
1058
+ * New Feature: Bulk importer allows you to create multiple feed sources at once.
1059
+ * Enhanced: Improved OPML importer with added hooks.
1060
+ * Enhanced: Centralized add-on licensing, fixing multiple bugs.
1061
+ * Fixed bug: Undefined `feed_limit` errors when using the shortcode.
1062
+
1063
+ = 4.4.4 (2014-08-19) =
1064
+ * Fixed bug: Errors when using older PHP versions 5.3 or lower.
1065
+
1066
+ = 4.4.3 (2014-08-19) =
1067
+ * Fixed bug: Errors when using older PHP versions 5.3 or lower.
1068
+
1069
+ = 4.4.2 (2014-08-19) =
1070
+ * Fixed bug: Errors when using older PHP versions 5.3 or lower.
1071
+
1072
+ = 4.4.1 (2014-08-18) =
1073
+ * Enhanced: Various improvements to the plugin interface and texts.
1074
+ * Enhanced: Moved the restore default settings button farther down the Debugging page, to avoid confusion with the delete button.
1075
+ * Fixed bug: Feed item dates were not being adjusted to the timezone when using a GMT offset.
1076
+ * Fixed bug: Feed item dates are now adjusted according to daylight savings time.
1077
+
1078
+ = 4.4 (2014-08-11) =
1079
+ * New Feature: Blacklist - delete items and blacklist them to never import them again.
1080
+ * Enhanced: Added a button in the Debugging page to reset the plugin settings to default.
1081
+ * Enhanced: WordPress Yoast SEO metaboxes and custom columns will no longer appear.
1082
+
1083
+ = 4.3.1 (2014-08-08) =
1084
+ * Enhanced: Better wording on settings page.
1085
+ * Fixed bug: The Links Behaviour option in the settings was not working.
1086
+ * Fixed bug: The wrong feed items were being shown for some sources when using the "View Items" row action.
1087
+
1088
+ = 4.3 (2014-08-04) =
1089
+ * New Feature: Feed items now also import authors.
1090
+ * Enhanced: Custom feed is now in RSS 2.0 format.
1091
+ * Enhanced: Improved the display template for feed items.
1092
+ * Fixed bug: Custom feed was not working in Firefox.
1093
+ * Fixed bug: Some feed items were showing items from another feed source.
1094
+ * Fixed bug: The feed limit in the global settings was not working.
1095
+
1096
+ = 4.2.3 (2014-07-29) =
1097
+ * Enhanced: Added an option to choose between the current pagination type, and numbered pagination.
1098
+ * Enhanced: The Feed Preview now also shows the total number of items in the feed.
1099
+ * Fixed bug: A PHP warning error was being shown in the System Info.
1100
+ * Fixed bug: Language files were not always being referenced correctly.
1101
+ * Fixed bug: Manually fetching a feed fails if the feed is scheduled to update in the next 10 minutes.
1102
+ * Fixed bug: Bing RSS feeds were importing duplicates on every update.
1103
+
1104
+ = 4.2.2 (2014-07-23) =
1105
+ * Enhanced: Facebook page feeds are now changed into RSS 2.0 feeds, rather than Atom 1.0 feeds.
1106
+ * Enhanced: Improved live updating performace on the Feed Sources page.
1107
+
1108
+ = 4.2.1 (2014-07-17) =
1109
+ * Enhanced: Feed Sources page is now more responsive.
1110
+
1111
+ = 4.2 (2014-07-17) =
1112
+ * New Feature: Can now view each feed source's imported feed items separate from other feed sources' feed items.
1113
+ * Enhanced: Major visual update to the Feed Sources page with new live updates.
1114
+ * Enhanced: The custom feed now includes the feed source.
1115
+ * Fixed bug: Google News feeds were importing duplicate items on every update.
1116
+ * Fixed bug: Multiple minor bug fixes with old filters.
1117
+
1118
+ = 4.1.6 (2014-06-28) =
1119
+ * Fixed bug: Results returned by wprss_get_feed_items_for_source() will no longer be affected by filters.
1120
+ * Fixed bug: Charset issue in titles
1121
+
1122
+ = 4.1.5 (2014-06-19) =
1123
+ * Enhanced: The Feed Sources table now indicates which feed sources encountered errors during the last import.
1124
+ * Fixed bug: Feed titles were not being decoded for HTML entities.
1125
+
1126
+ = 4.1.4 (2014-05-16) =
1127
+ * Enhanced: Minor improvements to feed importing and handling.
1128
+ * Fixed bug: HTML entities were not being decoded in feed item titles.
1129
+
1130
+ = 4.1.3 (2014-04-28) =
1131
+ * Enhanced: Added a force feed option, for valid RSS feeds with incorrect header content types.
1132
+ * Fixed bug: HTML entities in feed item titles are now being decoded.
1133
+
1134
+ = 4.1.2 (2014-04-22) =
1135
+ * Enhanced: Improved the custom feed, by allowing a custom title.
1136
+ * Enhanced: Improved shortcode, by adding the "pagination" parameter.
1137
+ * Enhanced: Modified a filter to fix some bugs in the add-ons.
1138
+
1139
+ = 4.1.1 (2014-04-09) =
1140
+ * Enhanced: Tracking notices only appear for admin users.
1141
+ * Fixed bug: Auto Feed Discovery was not working.
1142
+
1143
+ = 4.1 (2014-04-03) =
1144
+ * New Feature: Feed items can now link to enclosure links in the feed.
1145
+ * Enhanced: Added a filter to allow add-ons to modify feed item queries.
1146
+
1147
+ = 4.0.9 (2014-03-27) =
1148
+ * Enhanced: Added a filter to modify the feeds template.
1149
+ * Fixed bug: Nested lists in feeds template.
1150
+
1151
+ = 4.0.8 (2014-03-20) =
1152
+ * Fixed bug: Using the shortcode makes the comments section always open.
1153
+
1154
+ = 4.0.7 (2014-03-08) =
1155
+ * Fixed bug: The plugin prevented uploading of header images.
1156
+
1157
+ = 4.0.6 (2014-03-05) =
1158
+ * Fixed bug: Hook change in last version suspected reason for some installations having non-updated feed items.
1159
+
1160
+ = 4.0.5 (2014-03-03) =
1161
+ * New Feature: Time ago added as an option.
1162
+ * Enhanced: The plugin now allows the use of RSS and Atom feeds that do not specify the correct MIME type.
1163
+ * Enhanced: Better performance due to better hook usage.
1164
+ * Fixed bug: Facebook page feed URL conversion was not being triggered for new feed sources.
1165
+ * Fixed bug: Styles fix for pagination.
1166
+ * Fixed bug: Removed empty spaces in logging.
1167
+
1168
+ = 4.0.4 (2014-02-17) =
1169
+ * Enhanced: Added Activate/Pause bulk actions in the Feed Sources page.
1170
+ * Enhanced: Feed Sources page table has been re-designed.
1171
+ * Enhanced: Logging is now site dependant on multisite.
1172
+ * Fixed bug: Undefined display settings where appearing on the front end.
1173
+
1174
+ = 4.0.3 (2014-02-12) =
1175
+ * Fixed bug: The general setting for deleting feed items by age was not working.
1176
+
1177
+ = 4.0.2 (2014-02-10) =
1178
+ * Enhanced: Added a filter to change the html tags allowed in feed item content.
1179
+
1180
+ = 4.0.1 (2014-02-08) =
1181
+ * Fixed bug: Empty array of feed items bug caused importing problems.
1182
+
1183
+ = 4.0 (2014-02-04) =
1184
+ * Enhanced: Improved some internal queries, for better performance.
1185
+ * Fixed bug: Feed limits were not working properly.
1186
+
1187
+ = 3.9.9 (2014-02-03) =
1188
+ * Enhanced: The custom feed can now be extended by add-ons.
1189
+
1190
+ = 3.9.8 (2014-01-20) =
1191
+ * Fixed bug: Removed excessive logging from Debugging Error Log.
1192
+
1193
+ = 3.9.7 (2014-01-17) =
1194
+ * Fixed bug: Bug in admin-debugging.php causing trouble with admin login
1195
+
1196
+ = 3.9.6 (2014-01-17) =
1197
+ * Enhanced: Added error logging.
1198
+
1199
+ = 3.9.5 (2014-01-02) =
1200
+ * Enhanced: Added a feed validator link in the New/Edit Feed Sources page.
1201
+ * Enhanced: The Next Update column also shows the time remaining for next update, for feed source on the global update interval.
1202
+ * Enhanced: The custom feed has been improved, and is now identical to the feeds displayed with the shortcode.
1203
+ * Enhanced: License notifications only appear on the main site when using WordPress multisite.
1204
+ * Enhanced: Updated Colorbox script to 1.4.33
1205
+ * Fixed bug: The Imported Items column was always showing zero.
1206
+ * Fixed bug: Feed items not being imported with limit set to zero. Should be unlimited.
1207
+ * Fixed bug: Fat header in Feed Sources page
1208
+
1209
+ = 3.9.4 (2013-12-24) =
1210
+ * Enhanced: Added a column in the Feed Sources page that shows the number of feed items imported for each feed source.
1211
+ * Fixed bug: Leaving the delete old feed items empty did not ignore the delete.
1212
+
1213
+ = 3.9.3 (2013-12-23) =
1214
+ * Fixed bug: Fixed tracking pointer appearing on saving settings.
1215
+
1216
+ = 3.9.2 (2013-12-21) =
1217
+ * Fixed bug: Incorrect file include call.
1218
+
1219
+ = 3.9.1 (2013-12-12) =
1220
+ * Enhanced: Improved date and time handling for imported feed items.
1221
+ * Fixed bug: Incorrect values being shown in the Feed Processing metabox.
1222
+ * Fixed bug: Feed limits set to zero were causing feeds to not be imported.
1223
+
1224
+ = 3.9 (2013-12-12) =
1225
+ * New Feature: Feed sources can have their own update interval.
1226
+ * New Feature: The time remaining until the next update has been added to the Feed Source table.
1227
+
1228
+ = 3.8 (2013-12-05) =
1229
+ * New Feature: Feed items can be limited and deleted by their age.
1230
+ * Enhanced: Added utility functions for shorter filters.
1231
+ * Fixed bug: License codes were being erased when add-ons were deactivated.
1232
+ * Fixed bug: Some feed sources could not be set to active from the table controls.
1233
+ * Fixed bug: str_pos errors appear when custom feed url is left empty.
1234
+ * Fixed bug: Some options were producing undefined index errors.
1235
+
1236
+ = 3.7 (2013-11-28) =
1237
+ * New Feature: State system - Feed sources can be activated/paused.
1238
+ * New Feature: State system - Feed sources can be set to activate or pause themselves at a specific date and time.
1239
+ * Enhanced: Added compatibility with nested outline elements in OPML files.
1240
+ * Enhanced: Admin menu icon image will change into a Dashicon, when WordPress is updated to 3.8 (Decemeber 2013).
1241
+ * Fixed bug: Custom Post types were breaking when the plugin is activated.
1242
+
1243
+ = 3.6.1 (2013-11-17) =
1244
+ * Fixed bug: Missing 2nd argument for wprss_shorten_title()
1245
+
1246
+ = 3.6 (2013-11-16) =
1247
+ * New Feature: Can set the maximum length for titles. Long titles get trimmed.
1248
+ * Fixed bug: Fixed errors with undefined indexes for unchecked checkboxes in the settings page.
1249
+ * Fixed bug: Pagination on front static page was not working.
1250
+
1251
+ = 3.5.2 (2013-11-11) =
1252
+ * Fixed bug: Invalid feed source url was producing an Undefined method notice.
1253
+ * Fixed bug: Custom feed was producing a 404 page.
1254
+ * Fixed bug: Presstrends code firing on admin_init, opt-in implementation coming soon
1255
+
1256
+ = 3.5.1 (2013-11-09) =
1257
+ * Enhanced: Increased compatibility with RSS sources.
1258
+ * Fixed bug: Pagination not working on home page
1259
+
1260
+ = 3.5 (2013-11-6) =
1261
+ * New Feature: Can delete feed items for a particular source
1262
+ * Enhanced: the 'Fetch feed items' row action for feed sources resets itself after 3.5 seconds.
1263
+ * Enhanced: The feed image is saved for each url.
1264
+ * Fixed bug: Link to source now links to correct url. Previously linked to site's feed.
1265
+
1266
+ = 3.4.6 (2013-11-1) =
1267
+ * Enhanced: Added more hooks to debugging page for the Feed to Post add-on.
1268
+ * Fixed bug: Uninitialized loop index
1269
+
1270
+ = 3.4.5 (2013-10-30) =
1271
+ * Bug Fix: Feed items were not being imported while the WPML plugin was active.
1272
+
1273
+ = 3.4.4 (2013-10-26) =
1274
+ * New feature: Pagination
1275
+ * New feature: First implementation of editor button for easy shortcode creation
1276
+ * Enhanced: Feed items and sources don't show up in link manager
1277
+ * Enhanced: Included Presstrends code for plugin usage monitoring
1278
+
1279
+ = 3.4.3 (2013-10-20) =
1280
+ * Fixed bug: Removed anonymous functions for backwards PHP compatibility
1281
+ * Bug fix: Added suppress_filters in feed-display.php to prevent a user reported error
1282
+ * Bug fix: Missing <li> in certain feed displays
1283
+
1284
+ = 3.4.2 (2013-9-19) =
1285
+ * Enhanced: Added some hooks for Feed to Post compatibility
1286
+ * Enhanced: Moved date settings to a more appropriate location
1287
+
1288
+ = 3.4.1 (2013-9-16) =
1289
+ * Fixed Bug: Minor issue with options page - PHP notice
1290
+
1291
+ = 3.4 (2013-9-15) =
1292
+ * New Feature: Saving/Updating a feed source triggers an update for that source's feed items.
1293
+ * New Feature: Option to change Youtube, Vimeo and Dailymotion feed item URLs to embedded video players URLs
1294
+ * New Feature: Facebook Pages URLs are automatically detected and changed into Atom Feed URLs using FB's Graph
1295
+ * Enhanced: Updated jQuery Colorbox library to 1.4.29
1296
+ * Fixed Bug: Some settings did not have a default value set, and were throwing an 'Undefined Index' error
1297
+ * Fixed Bug: Admin notices do not disappear immediately when dismissed.
1298
+
1299
+ = Version 3.3.3 (2013-09-08) =
1300
+ * Fixed bug: Better function handling on uninstall, should remove uninstall issues
1301
+
1302
+ = Version 3.3.2 (2013-09-07) =
1303
+ * New feature: Added exclude parameter to shortcode
1304
+ * Enhanced: Added metabox links to documentation and add-ons
1305
+ * Fixed bug: Custom feed linking to post on user site rather than original source
1306
+ * Fixed bug: Custom post types issues when activitating the plugin
1307
+
1308
+ = Version 3.3.1 (2013-08-09) =
1309
+ * Fixed Bug: Roles and Capabilities file had not been included
1310
+ * Fixed Bug: Error on install, function not found
1311
+
1312
+ = Version 3.3 (2013-08-08) =
1313
+ * New feature: OPML importer
1314
+ * New feature: Feed item limits for individual Feed Sources
1315
+ * New feature: Custom feed URL
1316
+ * New feature: Feed limit on custom feed
1317
+ * New feature: New 'Fetch feed items' action for each Feed Source in listing display
1318
+ * New feature: Option to enable link to source
1319
+ * Enhanced: Date strings now change according to locale being used (i.e. compatible with WPML)
1320
+ * Enhanced: Capabilities implemented
1321
+ * Enhanced: Feed Sources row action 'View' removed
1322
+ * Fixed Bug: Proxy feed URLs resulting in the permalink: example.com/url
1323
+
1324
+ = Version 3.2 (2013-07-06) =
1325
+ * New feature: Parameter to limit number of feeds displayed
1326
+ * New feature: Paramter to limit feeds displayed to particular sources (via ID)
1327
+ * Enhanced: Better feed import handling to handle large number of feed sources
1328
+
1329
+ = Version 3.1.1 (2013-06-06) =
1330
+ * Fixed bug: Incompatibility with some other plugins due to function missing namespace
1331
+
1332
+ = Version 3.1 (2013-06-06) =
1333
+ * New feature: Option to set the number of feed items imported from every feed (default 5)
1334
+ * New feature: Import and Export aggregator settings and feed sources
1335
+ * New feature: Debugging page allowing manual feed refresh and feed reset
1336
+ * Enhanced: Faster handling of restoring sources from trash when feed limit is 0
1337
+ * Fixed bug: Limiter on number of overall feeds stored not working
1338
+ * Fixed bug: Incompatibility issue with Foobox plugin fixed
1339
+ * Fixed bug: Duplicate feeds sometimes imported
1340
+
1341
+ = Version 3.0 (2013-03-16) =
1342
+ * New feature: Option to select cron frequency
1343
+ * New feature: Code extensibility added to be compatible with add-ons
1344
+ * New feature: Option to set a limit to the number of feeds stored (previously 50, hard coded)
1345
+ * New feature: Option to define the format of the date shown below each feed item
1346
+ * New feature: Option to show or hide source of feed item
1347
+ * New feature: Option to show or hide publish date of feed item
1348
+ * New feature: Option to set text preceding publish date
1349
+ * New feature: Option to set text preceding source of feed item
1350
+ * New feature: Option to link title or not
1351
+ * New feature: Limit of 5 items imported for each source instead of 10
1352
+ * Enhanced: Performance improvement when publishing * New feeds in admin
1353
+ * Enhanced: Query tuning for better performance
1354
+ * Enhanced: Major code rewrite, refactoring and inclusion of hooks
1355
+ * Enhanced: Updated Colorbox to v1.4.1
1356
+ * Enhanced: Better security implementations
1357
+ * Enhanced: Better feed preview display
1358
+ * Fixed bug: Deletion of items upon source deletion not working properly
1359
+ * Requires: WordPress 3.3
1360
+
1361
+ = Version 2.2.3 (2012-11-01) =
1362
+ * Fixed bug: Tab navigation preventing typing in input boxes
1363
+ * Removed: Feeds showing up in internal linking pop up
1364
+
1365
+ = Version 2.2.2 (2012-10-30) =
1366
+ * Removed: Feeds showing up in site search results
1367
+ * Enhanced: Better tab button navigation when adding a new feed
1368
+ * Enhanced: Better guidance when a feed URL is invalid
1369
+
1370
+ = Version 2.2.1 (2012-10-17) =
1371
+ * Fixed bug: wprss_feed_source_order assumes everyone is an admin
1372
+
1373
+ = Version 2.2 (2012-10-01) =
1374
+ * Italian translation added
1375
+ * Feed source order changed to alphabetical
1376
+ * Fixed bug - repeated entries when having a non-valid feed source
1377
+ * Fixed bug - all imported feeds deleted upon trashing a single feed source
1378
+
1379
+ = Version 2.1 (2012-09-27) =
1380
+ * Now localised for translations
1381
+ * Fixed bug with date string
1382
+ * Fixed $link_before and $link_after, now working
1383
+ * Added backwards compatibility for wp_rss_aggregator() function
1384
+
1385
+ = Version 2.0 (2012-09-21) =
1386
+ * Bulk of code rewritten and refactored
1387
+ * Added install and upgrade functions
1388
+ * Added DB version setting
1389
+ * Feed sources now stored as Custom Post Types
1390
+ * Feed source list sortable ascending or descending by name
1391
+ * Removed days subsections in feed display
1392
+ * Ability to limit total number of feeds displayed
1393
+ * Feeds now fetched via Cron
1394
+ * Cron job to delete old feed items, keeps max of 50 items in DB
1395
+ * Now requires WordPress 3.2
1396
+ * Updated colorbox to v1.3.20.1
1397
+ * Limit of 15 items max imported for each source
1398
+ * Fixed issue of page content displaying incorrectly after feeds
1399
+
1400
+ = Version 1.1 (2012-08-13) =
1401
+ * Now requires WordPress 3.0
1402
+ * More flexible fetching of images directory
1403
+ * Has its own top level menu item
1404
+ * Added settings section
1405
+ * Ability to open in lightbox, new window or default browser behaviour
1406
+ * Ability to set links as follow or no follow
1407
+ * Using constants for oftenly used locations
1408
+ * Code refactoring
1409
+ * Changes in file and folder structure
1410
+
1411
+ = Version 1.0 (2012-01-06) =
1412
+ * Initial release.
wp-rss-aggregator.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: WP RSS Aggregator
4
  Plugin URI: http://www.wprssaggregator.com
5
  Description: Imports and aggregates multiple RSS Feeds using SimplePie
6
- Version: 4.7.7
7
  Author: Jean Galea
8
  Author URI: http://www.wprssaggregator.com
9
  License: GPLv2
@@ -29,7 +29,7 @@
29
 
30
  /**
31
  * @package WPRSSAggregator
32
- * @version 4.7.7
33
  * @since 1.0
34
  * @author Jean Galea <info@wprssaggregator.com>
35
  * @copyright Copyright (c) 2012-2015, Jean Galea
@@ -43,7 +43,7 @@
43
 
44
  // Set the version number of the plugin.
45
  if( !defined( 'WPRSS_VERSION' ) )
46
- define( 'WPRSS_VERSION', '4.7.7', true );
47
 
48
  if( !defined( 'WPRSS_WP_MIN_VERSION' ) )
49
  define( 'WPRSS_WP_MIN_VERSION', '4.0', true );
@@ -114,6 +114,11 @@
114
  * Load required files.
115
  */
116
 
 
 
 
 
 
117
  /* Load install, upgrade and migration code. */
118
  require_once ( WPRSS_INC . 'update.php' );
119
 
@@ -234,11 +239,16 @@
234
  /* Load the fallbacks for mbstring */
235
  require_once ( WPRSS_INC . 'fallback-mbstring.php' );
236
 
 
 
 
237
 
238
  register_activation_hook( __FILE__ , 'wprss_activate' );
239
  register_deactivation_hook( __FILE__ , 'wprss_deactivate' );
240
 
241
 
 
 
242
  add_action( 'init', 'wprss_init' );
243
  /**
244
  * Initialise the plugin
@@ -247,11 +257,6 @@
247
  * @return void
248
  */
249
  function wprss_init() {
250
- //If user requested to download system info, generate the download.
251
- if ( isset( $_POST['wprss-sysinfo'] ) ) {
252
- do_action( 'wprss_download_sysinfo' );
253
- }
254
-
255
  do_action( 'wprss_init' );
256
  }
257
 
@@ -410,6 +415,7 @@
410
  WPRSS_TEXT_DOMAIN ), WPRSS_WP_MIN_VERSION ),
411
  'notice_type' => 'error'
412
  ));
 
413
  }
414
 
415
 
3
  Plugin Name: WP RSS Aggregator
4
  Plugin URI: http://www.wprssaggregator.com
5
  Description: Imports and aggregates multiple RSS Feeds using SimplePie
6
+ Version: 4.7.8
7
  Author: Jean Galea
8
  Author URI: http://www.wprssaggregator.com
9
  License: GPLv2
29
 
30
  /**
31
  * @package WPRSSAggregator
32
+ * @version 4.7.8
33
  * @since 1.0
34
  * @author Jean Galea <info@wprssaggregator.com>
35
  * @copyright Copyright (c) 2012-2015, Jean Galea
43
 
44
  // Set the version number of the plugin.
45
  if( !defined( 'WPRSS_VERSION' ) )
46
+ define( 'WPRSS_VERSION', '4.7.8', true );
47
 
48
  if( !defined( 'WPRSS_WP_MIN_VERSION' ) )
49
  define( 'WPRSS_WP_MIN_VERSION', '4.0', true );
114
  * Load required files.
115
  */
116
 
117
+ /* Autoloader for this plugin */
118
+ require_once ( WPRSS_INC . 'autoload.php' );
119
+ // Adding autoload paths
120
+ wprss_autoloader()->add('Aventura\\Wprss\\Core', WPRSS_INC);
121
+
122
  /* Load install, upgrade and migration code. */
123
  require_once ( WPRSS_INC . 'update.php' );
124
 
239
  /* Load the fallbacks for mbstring */
240
  require_once ( WPRSS_INC . 'fallback-mbstring.php' );
241
 
242
+ // Initializes licensing
243
+ // Needs autoloading
244
+ // add_action( 'plugins_loaded', 'wprss_init_licensing' );
245
 
246
  register_activation_hook( __FILE__ , 'wprss_activate' );
247
  register_deactivation_hook( __FILE__ , 'wprss_deactivate' );
248
 
249
 
250
+
251
+
252
  add_action( 'init', 'wprss_init' );
253
  /**
254
  * Initialise the plugin
257
  * @return void
258
  */
259
  function wprss_init() {
 
 
 
 
 
260
  do_action( 'wprss_init' );
261
  }
262
 
415
  WPRSS_TEXT_DOMAIN ), WPRSS_WP_MIN_VERSION ),
416
  'notice_type' => 'error'
417
  ));
418
+
419
  }
420
 
421