RSS Post Importer - Version 2.1.3

Version Description

Download this release

Release Info

Developer koralyne
Plugin Icon 128x128 RSS Post Importer
Version 2.1.3
Comparing to
See all releases

Code changes from version 2.1.2 to 2.1.3

app/assets/css/style.css CHANGED
@@ -186,8 +186,9 @@
186
  }
187
  .rss_pi-table code {
188
  display: block;
189
- float: left;
190
- clear: both;
 
191
  padding: 3px 5px;
192
  font-size: 0.9em;
193
  background: none;
186
  }
187
  .rss_pi-table code {
188
  display: block;
189
+ display: inline-block;
190
+ /*float: left;*/
191
+ /*clear: both;*/
192
  padding: 3px 5px;
193
  font-size: 0.9em;
194
  background: none;
app/classes/admin/class-rss-pi-admin-processor.php CHANGED
@@ -43,11 +43,21 @@ class rssPIAdminProcessor {
43
  // formulate the feeds array
44
  $feeds = $this->process_feeds($ids);
45
 
46
- // import settings
47
  if ( isset($_FILES['import_csv']) && $settings['is_key_valid'] ) {
48
  $feeds = $this->import_csv($feeds);
49
  }
50
 
 
 
 
 
 
 
 
 
 
 
51
  // save and reload the options
52
  $this->save_reload_options($settings, $feeds);
53
 
@@ -58,7 +68,8 @@ class rssPIAdminProcessor {
58
  'settings-updated' => 'true',
59
  // yield the routine for import feeds via AJAX when needed
60
  'import' => ( $_POST['save_to_db'] == 'true' ),
61
- 'message' => $invalid_api_key ? 2 : 1
 
62
  ),
63
  $rss_post_importer->page_link
64
  ));
@@ -253,7 +264,7 @@ class rssPIAdminProcessor {
253
  'category_id' => (isset($_POST[$id . '-category_id'])) ? $_POST[$id . '-category_id'] : '',
254
  'tags_id' => (isset($_POST[$id . '-tags_id'])) ? $_POST[$id . '-tags_id'] : '',
255
  'keywords' => array_map('trim',$keywords),
256
- 'strip_html' => $_POST[$id . '-strip_html']
257
  ));
258
  }
259
  }
43
  // formulate the feeds array
44
  $feeds = $this->process_feeds($ids);
45
 
46
+ // import CSV file
47
  if ( isset($_FILES['import_csv']) && $settings['is_key_valid'] ) {
48
  $feeds = $this->import_csv($feeds);
49
  }
50
 
51
+ // import OPML file
52
+ // @since v2.1.3
53
+ if ( isset($_FILES['import_opml']) && is_uploaded_file($_FILES['import_opml']['tmp_name']) ) {
54
+ $opml = new Rss_pi_opml();
55
+ $feeds = $opml->import($feeds);
56
+ $opml_errors = $opml->errors;
57
+ } else {
58
+ $opml_errors = array();
59
+ }
60
+
61
  // save and reload the options
62
  $this->save_reload_options($settings, $feeds);
63
 
68
  'settings-updated' => 'true',
69
  // yield the routine for import feeds via AJAX when needed
70
  'import' => ( $_POST['save_to_db'] == 'true' ),
71
+ 'message' => $invalid_api_key ? 2 : 1,
72
+ // 'opml_errors' => $opml_errors ? urlencode(implode('<br/>',$opml_errors)) : '',
73
  ),
74
  $rss_post_importer->page_link
75
  ));
264
  'category_id' => (isset($_POST[$id . '-category_id'])) ? $_POST[$id . '-category_id'] : '',
265
  'tags_id' => (isset($_POST[$id . '-tags_id'])) ? $_POST[$id . '-tags_id'] : '',
266
  'keywords' => array_map('trim',$keywords),
267
+ 'strip_html' => (isset($_POST[$id . '-strip_html'])) ? $_POST[$id . '-strip_html'] : ''
268
  ));
269
  }
270
  }
app/classes/admin/class-rss-pi-admin.php CHANGED
@@ -95,7 +95,7 @@ class rssPIAdmin {
95
 
96
  // manage meta data on post deletion and restoring
97
  add_action('wp_trash_post', array($this, 'delete_post')); // trashing a post
98
- add_action('before_delete_post', array($this, 'delete_post')); // deleting a post permanently
99
  add_action('untrash_post', array($this, 'restore_post')); // restoring a post from trash
100
 
101
  // the ajax for adding new feeds (table rows)
@@ -112,6 +112,13 @@ class rssPIAdmin {
112
 
113
  // Add 10 minutes in frequency.
114
  add_filter('cron_schedules', array($this, 'rss_pi_cron_add'));
 
 
 
 
 
 
 
115
  }
116
 
117
  /**
@@ -209,8 +216,12 @@ class rssPIAdmin {
209
  */
210
  function screen() {
211
 
 
 
 
 
212
  // display a success message
213
- if( isset($_GET['deleted_cache_purged']) || isset($_GET['settings-updated']) || isset($_GET['invalid_api_key']) || isset($_GET['import']) && $_GET['settings-updated'] ) {
214
  ?>
215
  <div id="message" class="updated">
216
  <?php
@@ -224,17 +235,18 @@ class rssPIAdmin {
224
  <p><strong><?php _e('Settings saved.') ?></strong></p>
225
  <?php
226
  }
227
- if( isset($_GET['imported']) && $_GET['imported'] ) {
228
- $imported = intval($_GET['imported']);
229
- ?>
 
230
  <p><strong><?php printf( _n( '%s new post imported.', '%s new posts imported.', $imported, 'rss_pi' ), $imported ); ?></strong></p>
231
- <?php
232
- }
233
  ?>
234
  </div>
235
  <?php
236
- // import feeds via AJAX
237
- if( isset($_GET['import']) ) {
238
  ?>
239
  <script type="text/javascript">
240
  <?php
@@ -245,8 +257,8 @@ if ( is_array($this->options['feeds']) ) :
245
  endforeach;
246
  endif;
247
  ?>
248
- if (feeds !== undefined) {
249
- feeds.set(<?php echo json_encode($ids); ?>);
250
  } else {
251
  var feeds = <?php echo json_encode($ids); ?>;
252
  }
@@ -255,7 +267,7 @@ if (feeds !== undefined) {
255
  }
256
  }
257
 
258
- // display a error message
259
  if( isset($_GET['message']) && $_GET['message'] > 1 ) {
260
  ?>
261
  <div id="message" class="error">
@@ -267,17 +279,13 @@ if (feeds !== undefined) {
267
  <p><strong><?php _e('Invalid API key!', 'rss_api'); ?></strong></p>
268
  <?php
269
  }
270
- break;
271
  }
272
  ?>
273
  </div>
274
  <?php
275
  }
276
 
277
- // it'll process any submitted form data
278
- // reload the options just in case
279
- $this->load_options();
280
-
281
  global $rss_post_importer;
282
 
283
  // include the template for the ui
@@ -321,6 +329,8 @@ if (feeds !== undefined) {
321
  function ajax_import() {
322
  global $rss_post_importer;
323
 
 
 
324
  // if there's nothing for processing or invalid data, bail
325
  if ( ! isset($_POST['feed']) ) {
326
  wp_send_json_error(array('message'=>'no feed provided'));
@@ -375,6 +385,11 @@ if (feeds !== undefined) {
375
 
376
  remove_filter('wp_feed_cache_transient_lifetime', array($engine, 'frequency'));
377
 
 
 
 
 
 
378
  // reformulate import count
379
  $imports = intval($this->options['imports']) + $post_count;
380
 
95
 
96
  // manage meta data on post deletion and restoring
97
  add_action('wp_trash_post', array($this, 'delete_post')); // trashing a post
98
+ // add_action('before_delete_post', array($this, 'delete_post')); // deleting a post permanently
99
  add_action('untrash_post', array($this, 'restore_post')); // restoring a post from trash
100
 
101
  // the ajax for adding new feeds (table rows)
112
 
113
  // Add 10 minutes in frequency.
114
  add_filter('cron_schedules', array($this, 'rss_pi_cron_add'));
115
+
116
+ // trigger on Export
117
+ if ( isset($_POST['export_opml']) ) {
118
+ $this->opml = new Rss_pi_opml();
119
+ $this->opml->export();
120
+ }
121
+
122
  }
123
 
124
  /**
216
  */
217
  function screen() {
218
 
219
+ // it'll process any submitted form data
220
+ // reload the options just in case
221
+ $this->load_options();
222
+
223
  // display a success message
224
+ if ( isset($_GET['deleted_cache_purged']) || isset($_GET['settings-updated']) || isset($_GET['invalid_api_key']) || isset($_GET['import']) && @$_GET['settings-updated'] ) {
225
  ?>
226
  <div id="message" class="updated">
227
  <?php
235
  <p><strong><?php _e('Settings saved.') ?></strong></p>
236
  <?php
237
  }
238
+ // OBSOLETE, we're now doing this via AJAX
239
+ // if( isset($_GET['imported']) && $_GET['imported'] ) {
240
+ // $imported = intval($_GET['imported']);
241
+ /*?>
242
  <p><strong><?php printf( _n( '%s new post imported.', '%s new posts imported.', $imported, 'rss_pi' ), $imported ); ?></strong></p>
243
+ <?php*/
244
+ // }
245
  ?>
246
  </div>
247
  <?php
248
+ // import feeds via AJAX but only when Save is done
249
+ if( isset($_GET['import']) && isset($_GET['settings-updated']) && $_GET['settings-updated'] ) {
250
  ?>
251
  <script type="text/javascript">
252
  <?php
257
  endforeach;
258
  endif;
259
  ?>
260
+ if ( feeds !== undefined ) {
261
+ feeds.set( <?php echo json_encode($ids); ?> );
262
  } else {
263
  var feeds = <?php echo json_encode($ids); ?>;
264
  }
267
  }
268
  }
269
 
270
+ // display an error message
271
  if( isset($_GET['message']) && $_GET['message'] > 1 ) {
272
  ?>
273
  <div id="message" class="error">
279
  <p><strong><?php _e('Invalid API key!', 'rss_api'); ?></strong></p>
280
  <?php
281
  }
282
+ // break;
283
  }
284
  ?>
285
  </div>
286
  <?php
287
  }
288
 
 
 
 
 
289
  global $rss_post_importer;
290
 
291
  // include the template for the ui
329
  function ajax_import() {
330
  global $rss_post_importer;
331
 
332
+ $this->load_options();
333
+
334
  // if there's nothing for processing or invalid data, bail
335
  if ( ! isset($_POST['feed']) ) {
336
  wp_send_json_error(array('message'=>'no feed provided'));
385
 
386
  remove_filter('wp_feed_cache_transient_lifetime', array($engine, 'frequency'));
387
 
388
+ if ( $items === false ) {
389
+ // there were an wp_error doing fetch_feed
390
+ wp_send_json_error(array('url'=>$f['url']));
391
+ }
392
+
393
  // reformulate import count
394
  $imports = intval($this->options['imports']) + $post_count;
395
 
app/classes/admin/class-rss-pi-opml.php ADDED
@@ -0,0 +1,216 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * This class handles all OPML functionality
5
+ *
6
+ * @author mobilova UG (haftungsbeschränkt) <rsspostimporter@feedsapi.com>
7
+ */
8
+ if (!class_exists("Rss_pi_opml")) {
9
+
10
+ class Rss_pi_opml {
11
+
12
+ var $options;
13
+
14
+ var $errors;
15
+
16
+ /*
17
+ * The constructor
18
+ */
19
+ function __construct() {
20
+
21
+ $this->options = get_option('rss_pi_feeds', array());
22
+ }
23
+
24
+ /*
25
+ * Exports all feeds, no Settings exported
26
+ */
27
+ function export() {
28
+
29
+ if ( $this->options['settings']['is_key_valid'] ) {
30
+
31
+ $feeds = $this->options['feeds'];
32
+ $title = get_option('blogname');
33
+ $ownerEmail = get_option('admin_email');
34
+
35
+ if ( ! count($feeds) || ! trim($title) || ! $ownerEmail ) {
36
+ return;
37
+ }
38
+
39
+ if ( ! $title || ! $ownerEmail ) {
40
+ return;
41
+ }
42
+
43
+ $output = '';
44
+
45
+ $output .= $this->_header($title, $ownerEmail);
46
+
47
+ foreach ( $feeds as $feed ) {
48
+ $output .= $this->_entry($feed['url'], $feed['name']);
49
+ }
50
+
51
+ $output .= $this->_footer();
52
+
53
+ $filename = "rss_pi_export_" . date("Y-m-d") . ".opml";
54
+ $this->_send_headers($filename);
55
+ echo "\xEF\xBB\xBF";
56
+ print($output);
57
+ die();
58
+
59
+ }
60
+
61
+ }
62
+
63
+ /*
64
+ * Imports feeds from file
65
+ */
66
+ function import($feeds) {
67
+
68
+ if ( $this->options['settings']['is_key_valid'] ) {
69
+
70
+ $file = $_FILES['import_opml']['tmp_name'];
71
+ $opml = file_get_contents($file);
72
+ $opml = new OPMLParser($opml);
73
+ @unlink($file);
74
+
75
+ $feeds = $this->_parse_data( $opml->data, $feeds );
76
+ $this->options['feeds'] = $feeds;
77
+
78
+ }
79
+
80
+ return $feeds;
81
+
82
+ }
83
+
84
+ private function _parse_data($data, $feeds) {
85
+
86
+ if ( ! is_array($data) ) {
87
+ return $feeds;
88
+ }
89
+
90
+ foreach ( $data as $k => $item ) {
91
+
92
+ if ( isset($item['xmlurl']) && isset($item['text']) && trim($item['xmlurl']) !== '' ) {
93
+
94
+ // if ( $feed['xmlurl'] && filter_var($feed['xmlurl'], FILTER_VALIDATE_URL) && $this->_feed_url_exists( $feeds, $feed['xmlurl'] ) )
95
+ if ( $this->_feed_url_exists( $feeds, $item['xmlurl'] ) ) {
96
+ $this->errors[] = 'Duplicate Feed url: ' . $item['xmlurl'];
97
+ continue;
98
+ }
99
+ if ( $this->_feed_name_exists( $feeds, $item['text'] ) ) {
100
+ $this->errors[] = 'Duplicate Feed name: ' . $item['text'];
101
+ continue;
102
+ }
103
+
104
+ $c = count($feeds);
105
+ array_push($feeds, array(
106
+ 'id' => uniqid("54d4c" . $c),
107
+ 'url' => $item['xmlurl'],
108
+ 'name' => $item['text'],
109
+ // default values
110
+ 'max_posts' => 10,
111
+ 'author_id' => get_current_user_id(),
112
+ 'category_id' => array(1),
113
+ 'tags_id' => '',
114
+ 'keywords' => '',
115
+ 'strip_html' => 'false',
116
+ ));
117
+
118
+ } else {
119
+
120
+ $feeds = $this->_parse_data($item, $feeds);
121
+
122
+ }
123
+
124
+ }
125
+
126
+ return $feeds;
127
+
128
+ }
129
+
130
+ private function _feed_url_exists($feeds, $url) {
131
+
132
+ if ( ! empty($feeds) && ! empty($url) ) {
133
+ for ( $i = 0; $i < count($feeds); $i++ ) {
134
+ if ( $feeds[$i]['url'] == $url ) {
135
+ return true;
136
+ }
137
+ }
138
+ return false;
139
+ }
140
+ }
141
+
142
+ private function _feed_name_exists($feeds, $name) {
143
+
144
+ if ( ! empty($feeds) && ! empty($name) ) {
145
+ for ( $i = 0; $i < count($feeds); $i++ ) {
146
+ if ( $feeds[$i]['name'] == $name ) {
147
+ return true;
148
+ }
149
+ }
150
+ return false;
151
+ }
152
+ }
153
+
154
+ /**
155
+ * basic opml header
156
+ * @param string $opmlTitle
157
+ * @param string $opmlOwnerEmail
158
+ * @return string
159
+ */
160
+ private function _header($title, $ownerEmail) {
161
+ // $result = "<-?xml version=\"1.0\" encoding=\"ISO-8859-1\"?->\n"
162
+ $result = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n"
163
+ . "<opml version=\"1.1\">\n"
164
+ . " <head>\n"
165
+ . " <title>" . $title . "</title>\n"
166
+ . " <dateCreated>" . date("r") . "</dateCreated>\n"
167
+ // . " <ownerName>" . $ownerName . "</ownerName>\n"
168
+ . " <ownerEmail>" . $ownerEmail . "</ownerEmail>\n"
169
+ . " </head>\n"
170
+ . " <body>\n";
171
+ return $result;
172
+ }
173
+
174
+ /**
175
+ * just returns a test footer
176
+ * @return string
177
+ */
178
+ private function _footer() {
179
+ $result = " </body>\n"
180
+ . "</opml>";
181
+ return $result;
182
+ }
183
+
184
+ /**
185
+ * creates an XML entry for the OPML file
186
+ * @param string $feedURL
187
+ * @param string $feedTitle
188
+ * @return string
189
+ */
190
+ private function _entry($feedURL, $feedTitle) {
191
+ $result = " <outline text=\"" . $feedTitle . "\" type=\"rss\" xmlUrl=\"" . $feedURL . "\"/>\n";
192
+ return $result;
193
+ }
194
+
195
+ private function _send_headers($filename) {
196
+
197
+ // disable caching
198
+ $now = gmdate("D, d M Y H:i:s");
199
+ header("Expires: Tue, 01 Jan 2000 01:00:00 GMT"); // a date in the past
200
+ header("Cache-Control: max-age=0, no-cache, must-revalidate, proxy-revalidate");
201
+ header("Last-Modified: {$now} GMT");
202
+
203
+ // force download
204
+ header("Content-Type: application/force-download");
205
+ header("Content-Type: application/octet-stream");
206
+ header("Content-Type: application/download");
207
+
208
+ // disposition / encoding on response body
209
+ header("Content-Disposition: attachment;filename={$filename}");
210
+ header("Content-Transfer-Encoding: binary");
211
+
212
+ }
213
+
214
+ }
215
+ // CLass Rss_pi_opml
216
+ }
app/classes/helpers/class-OPMLParser.php ADDED
@@ -0,0 +1,94 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * OPML Parser
4
+ *
5
+ * A simple OPML parser for PHP 5, using the SimpleXMLElement class.
6
+ *
7
+ * Copyright (c) 2008, Ryan McCue
8
+ * All rights reserved.
9
+ *
10
+ * Redistribution and use in source and binary forms, with or without modification, are
11
+ * permitted provided that the following conditions are met:
12
+ *
13
+ * * Redistributions of source code must retain the above copyright notice, this list of
14
+ * conditions and the following disclaimer.
15
+ *
16
+ * * Redistributions in binary form must reproduce the above copyright notice, this list
17
+ * of conditions and the following disclaimer in the documentation and/or other materials
18
+ * provided with the distribution.
19
+ *
20
+ * * Neither the name of Lilina nor the names of its contributors may be used
21
+ * to endorse or promote products derived from this software without specific prior
22
+ * written permission.
23
+ *
24
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
25
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
26
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS
27
+ * AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
29
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
30
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
31
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32
+ * POSSIBILITY OF SUCH DAMAGE.
33
+ *
34
+ * @author Ryan McCue
35
+ * @package OPML Parser
36
+ * @link http://cubegames.net/code/opml-parser/ OPML Parser Homepage
37
+ * @license http://www.opensource.org/licenses/bsd-license.php BSD License
38
+ */
39
+
40
+ /**
41
+ * OPML Parser class
42
+ *
43
+ * @package OPML Parser
44
+ */
45
+ class OPMLParser {
46
+
47
+ public $data = array();
48
+ public $raw = '';
49
+ public $error = '';
50
+
51
+ public function __construct($raw_data) {
52
+ $this->raw = $raw_data;
53
+ // Create an XML parser
54
+ try {
55
+ $xml_parser = new SimpleXMLElement($this->raw, LIBXML_NOERROR);
56
+
57
+ $this->data = $this->loop($xml_parser->body->outline);
58
+ } catch (Exception $e) {
59
+ $this->error = $e->getMessage();
60
+ return;
61
+ }
62
+ }
63
+
64
+ protected function loop($element) {
65
+ $data = array();
66
+
67
+ foreach ($element as $element) {
68
+ if ($element['type'] == 'rss' || isset($element['xmlUrl'])) {
69
+ $data[] = $this->format($element);
70
+ } elseif ($element->outline) {
71
+ $data[(string) $element['text']] = $this->loop($element->outline);
72
+ }
73
+ }
74
+
75
+ return $data;
76
+ }
77
+
78
+ /**
79
+ * Return an array from a supplied SimpleXMLElement object
80
+ *
81
+ * @param SimpleXMLElement $element
82
+ * @return array
83
+ */
84
+ protected function format($element) {
85
+ return array(
86
+ 'htmlurl' => (string) $element['htmlUrl'],
87
+ 'xmlurl' => (string) $element['xmlUrl'],
88
+ 'text' => (string) $element['text'],
89
+ 'title' => (string) $element['title'],
90
+ 'description' => (string) $element['description'],
91
+ );
92
+ }
93
+
94
+ }
app/classes/helpers/class-rss-pi-featured-image.php CHANGED
@@ -18,13 +18,13 @@ if (!function_exists('media_handle_sideload')) {
18
  class rssPIFeaturedImage {
19
 
20
  /**
21
- * Sets featured image
22
  *
23
  * @param object $item Feed item
24
  * @param int $post_id Post id
25
  * @return boolean
26
  */
27
- function _set($item, $post_id) {
28
 
29
  $content = $item->get_content() != "" ? $item->get_content() : $item->get_description();
30
 
@@ -70,6 +70,20 @@ class rssPIFeaturedImage {
70
  // sideload it
71
  $featured_id = $this->_sideload( $img_url, $post_id, '' );
72
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  if ( ! is_wp_error($featured_id) ) {
74
  // add_action('set_rss_pi_featured_image', $featured_id, $post_id);
75
  do_action( 'set_rss_pi_featured_image', $featured_id, $post_id );
18
  class rssPIFeaturedImage {
19
 
20
  /**
21
+ * Prepare featured image
22
  *
23
  * @param object $item Feed item
24
  * @param int $post_id Post id
25
  * @return boolean
26
  */
27
+ function _prepare($item, $post_id) {
28
 
29
  $content = $item->get_content() != "" ? $item->get_content() : $item->get_description();
30
 
70
  // sideload it
71
  $featured_id = $this->_sideload( $img_url, $post_id, '' );
72
 
73
+ return $featured_id;
74
+ }
75
+
76
+ /**
77
+ * Sets featured image
78
+ *
79
+ * @param object $item Feed item
80
+ * @param int $post_id Post id
81
+ * @return boolean
82
+ */
83
+ function _set($item, $post_id) {
84
+
85
+ $featured_id = $this->_prepare($item, $post_id);
86
+
87
  if ( ! is_wp_error($featured_id) ) {
88
  // add_action('set_rss_pi_featured_image', $featured_id, $post_id);
89
  do_action( 'set_rss_pi_featured_image', $featured_id, $post_id );
app/classes/import/class-rss-pi-engine.php CHANGED
@@ -30,7 +30,17 @@ class rssPIEngine {
30
 
31
  global $rss_post_importer;
32
 
33
- // load options
 
 
 
 
 
 
 
 
 
 
34
  $this->options = $rss_post_importer->options;
35
  }
36
 
@@ -42,6 +52,8 @@ class rssPIEngine {
42
  public function import_feed() {
43
  global $rss_post_importer;
44
 
 
 
45
  $post_count = 0;
46
 
47
  // filter cache lifetime
@@ -348,11 +360,13 @@ class rssPIEngine {
348
  } else {
349
  $tags_name = array();
350
  }
351
- $parser->_parse($item, $args['feed_title'], $args['strip_html']);
 
 
 
352
  $post = array(
353
  'post_title' => $item->get_title(),
354
- // parse the content
355
- 'post_content' => $parser->_parse($item, $args['feed_title'], $args['strip_html']),
356
  'post_status' => $this->options['settings']['post_status'],
357
  'post_author' => $args['author_id'],
358
  'post_category' => array($args['category_id']),
@@ -361,8 +375,6 @@ class rssPIEngine {
361
  'post_date' => get_date_from_gmt($item->get_date('Y-m-d H:i:s'))
362
  );
363
 
364
- $content = $post['post_content'];
365
-
366
  // catch base url and replace any img src with it
367
  if (preg_match('/src="\//ui', $content)) {
368
  preg_match('/href="(.+?)"/ui', $content, $matches);
@@ -381,7 +393,6 @@ class rssPIEngine {
381
 
382
  //download images and save them locally if setting suggests so
383
  if ($this->options['settings']['import_images_locally'] == 'true') {
384
-
385
  $post = $this->download_images_locally($post);
386
  }
387
 
@@ -389,8 +400,34 @@ class rssPIEngine {
389
  $post_id = $this->_insert($post, $item->get_permalink());
390
 
391
  // set thumbnail
392
- if ($this->options['settings']['disable_thumbnail'] == 'false') {
 
393
  $thumbnail->_set($item, $post_id);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
394
  }
395
 
396
  array_push($saved_posts, $post);
@@ -414,8 +451,9 @@ class rssPIEngine {
414
  $post_exists = FALSE;
415
 
416
  if ( isset($this->options['upgraded']['deleted_posts']) ) { // database migrated
417
- // check if there is a post with this source URL
418
- $posts = $wpdb->get_results( $wpdb->prepare( "SELECT meta_id FROM {$wpdb->postmeta} WHERE meta_key = 'rss_pi_source_md5' and meta_value = %s", $permalink_md5 ), 'ARRAY_A');
 
419
  if ( count($posts) ) {
420
  $post_exists = TRUE;
421
  }
@@ -533,7 +571,10 @@ class rssPIEngine {
533
  }
534
 
535
  function add_to_media($url, $associated_with_post, $desc) {
536
- $tmp = download_url($url);
 
 
 
537
  $post_id = $associated_with_post;
538
  $desc = $desc;
539
  $file_array = array();
30
 
31
  global $rss_post_importer;
32
 
33
+ $this->load_options();
34
+ }
35
+
36
+ /**
37
+ * Start the engine
38
+ *
39
+ * @global type $rss_post_importer
40
+ */
41
+ public function load_options() {
42
+
43
+ global $rss_post_importer;
44
  $this->options = $rss_post_importer->options;
45
  }
46
 
52
  public function import_feed() {
53
  global $rss_post_importer;
54
 
55
+ $this->load_options();
56
+
57
  $post_count = 0;
58
 
59
  // filter cache lifetime
360
  } else {
361
  $tags_name = array();
362
  }
363
+
364
+ // parse the content
365
+ $content = $parser->_parse($item, $args['feed_title'], $args['strip_html']);
366
+
367
  $post = array(
368
  'post_title' => $item->get_title(),
369
+ 'post_content' => $content,
 
370
  'post_status' => $this->options['settings']['post_status'],
371
  'post_author' => $args['author_id'],
372
  'post_category' => array($args['category_id']),
375
  'post_date' => get_date_from_gmt($item->get_date('Y-m-d H:i:s'))
376
  );
377
 
 
 
378
  // catch base url and replace any img src with it
379
  if (preg_match('/src="\//ui', $content)) {
380
  preg_match('/href="(.+?)"/ui', $content, $matches);
393
 
394
  //download images and save them locally if setting suggests so
395
  if ($this->options['settings']['import_images_locally'] == 'true') {
 
396
  $post = $this->download_images_locally($post);
397
  }
398
 
400
  $post_id = $this->_insert($post, $item->get_permalink());
401
 
402
  // set thumbnail
403
+ if ( $this->options['settings']['disable_thumbnail'] == 'false' ) {
404
+ // assign a thumbnail (featured image) to the post
405
  $thumbnail->_set($item, $post_id);
406
+ $attachment_id = get_post_thumbnail_id($post_id);
407
+ } else {
408
+ // just download the image to the media library
409
+ $attachment_id = $thumbnail->_prepare($item, $post_id);
410
+ }
411
+
412
+ /* Parse {$inline_image} template tag
413
+ * @since 2.1.3
414
+ */
415
+ if ( preg_match('/\{\$inline_image\}/i', $post['post_content']) ) {
416
+ $_post_content = $post['post_content'];
417
+ if ( $attachment_id ) {
418
+ // $featured_image = wp_get_attachment_image($attachment_id);
419
+ $featured_image = wp_get_attachment_image_src($attachment_id,'full');
420
+ $featured_image = '<img src="'.$featured_image[0].'" width="'.$featured_image[1].'" height="'.$featured_image[2].'">';
421
+ } else {
422
+ $featured_image = '';
423
+ }
424
+ $_post_content = preg_replace('/\{\$inline_image\}/i', $featured_image, $_post_content);
425
+ $_post = array(
426
+ 'ID' => $post_id,
427
+ 'post_content' => $_post_content
428
+ );
429
+ wp_update_post($_post);
430
+ $post['post_content'] = $_post_content;
431
  }
432
 
433
  array_push($saved_posts, $post);
451
  $post_exists = FALSE;
452
 
453
  if ( isset($this->options['upgraded']['deleted_posts']) ) { // database migrated
454
+ // check if there is a published (!) post with this source URL
455
+ // $posts = $wpdb->get_results( $wpdb->prepare( "SELECT meta_id FROM {$wpdb->postmeta} WHERE meta_key = 'rss_pi_source_md5' and meta_value = %s", $permalink_md5 ), 'ARRAY_A');
456
+ $posts = $wpdb->get_results( $wpdb->prepare( "SELECT meta_id FROM {$wpdb->postmeta} pm, {$wpdb->posts} p WHERE pm.meta_key = 'rss_pi_source_md5' and pm.meta_value = %s AND pm.post_id = p.ID AND p.post_status = 'publish'", $permalink_md5 ), 'ARRAY_A');
457
  if ( count($posts) ) {
458
  $post_exists = TRUE;
459
  }
571
  }
572
 
573
  function add_to_media($url, $associated_with_post, $desc) {
574
+ $tmp = @download_url($url);
575
+ if (is_wp_error($tmp)) {
576
+ return false;
577
+ }
578
  $post_id = $associated_with_post;
579
  $desc = $desc;
580
  $file_array = array();
app/templates/feed-table-row.php CHANGED
@@ -54,13 +54,13 @@ if (is_array($f['category_id'])) {
54
 
55
  <tr id="display_<?php echo ($f['id']); ?>" class="data-row<?php echo $show; ?>" data-fields="name,url,max_posts">
56
  <td class="rss_pi-feed_name">
57
- <strong><a href="#" class="toggle-edit" data-target="<?php echo ($f['id']); ?>"><span class="field-name"><?php echo $f['name']; ?></span></a></strong>
58
  <div class="row-options">
59
  <a href="#" class="toggle-edit" data-target="<?php echo ($f['id']); ?>"><?php _e('Edit', 'rss_pi'); ?></a> |
60
  <a href="#" class="delete-row" data-target="<?php echo ($f['id']); ?>"><?php _e('Delete', 'rss_pi'); ?></a>
61
  </div>
62
  </td>
63
- <td class="rss_pi-feed_url"><span class="field-url"><?php echo $f['url']; ?></span></td>
64
  <td class="rss_pi_feed_max_posts"><span class="field-max_posts"><?php echo $f['max_posts']; ?></span></td>
65
  <!-- <td width="20%"><?php //echo $category; ?></td>-->
66
  </tr>
@@ -70,7 +70,7 @@ if (is_array($f['category_id'])) {
70
  <tr>
71
  <td><label for="<?php echo ($f['id']); ?>-name"><?php _e("Feed name", 'rss_pi'); ?></label></td>
72
  <td>
73
- <input type="text" class="field-name" name="<?php echo ($f['id']); ?>-name" id="<?php echo ($f['id']); ?>-name" value="<?php echo ($f['name']); ?>" />
74
  </td>
75
  </tr>
76
  <tr>
@@ -78,7 +78,7 @@ if (is_array($f['category_id'])) {
78
  <label for="<?php echo ($f['id']); ?>-url"><?php _e("Feed url", 'rss_pi'); ?></label>
79
  <p class="description">e.g. "http://news.google.com/?output=rss"</p>
80
  </td>
81
- <td><input type="text" class="field-url" name="<?php echo ($f['id']); ?>-url" id="<?php echo ($f['id']); ?>-url" value="<?php echo ($f['url']); ?>" /></td>
82
  </tr>
83
  <tr>
84
  <td><label for="<?php echo ($f['id']); ?>-max_posts"><?php _e("Max posts / import", 'rss_pi'); ?></label></td>
54
 
55
  <tr id="display_<?php echo ($f['id']); ?>" class="data-row<?php echo $show; ?>" data-fields="name,url,max_posts">
56
  <td class="rss_pi-feed_name">
57
+ <strong><a href="#" class="toggle-edit" data-target="<?php echo ($f['id']); ?>"><span class="field-name"><?php echo esc_html(stripslashes($f['name'])); ?></span></a></strong>
58
  <div class="row-options">
59
  <a href="#" class="toggle-edit" data-target="<?php echo ($f['id']); ?>"><?php _e('Edit', 'rss_pi'); ?></a> |
60
  <a href="#" class="delete-row" data-target="<?php echo ($f['id']); ?>"><?php _e('Delete', 'rss_pi'); ?></a>
61
  </div>
62
  </td>
63
+ <td class="rss_pi-feed_url"><span class="field-url"><?php echo esc_url(stripslashes($f['url'])); ?></span></td>
64
  <td class="rss_pi_feed_max_posts"><span class="field-max_posts"><?php echo $f['max_posts']; ?></span></td>
65
  <!-- <td width="20%"><?php //echo $category; ?></td>-->
66
  </tr>
70
  <tr>
71
  <td><label for="<?php echo ($f['id']); ?>-name"><?php _e("Feed name", 'rss_pi'); ?></label></td>
72
  <td>
73
+ <input type="text" class="field-name" name="<?php echo ($f['id']); ?>-name" id="<?php echo ($f['id']); ?>-name" value="<?php echo esc_attr(stripslashes($f['name'])); ?>" />
74
  </td>
75
  </tr>
76
  <tr>
78
  <label for="<?php echo ($f['id']); ?>-url"><?php _e("Feed url", 'rss_pi'); ?></label>
79
  <p class="description">e.g. "http://news.google.com/?output=rss"</p>
80
  </td>
81
+ <td><input type="text" class="field-url" name="<?php echo ($f['id']); ?>-url" id="<?php echo ($f['id']); ?>-url" value="<?php echo esc_attr(stripslashes($f['url'])); ?>" /></td>
82
  </tr>
83
  <tr>
84
  <td><label for="<?php echo ($f['id']); ?>-max_posts"><?php _e("Max posts / import", 'rss_pi'); ?></label></td>
app/templates/settings-table.php CHANGED
@@ -53,6 +53,7 @@
53
  <dt><code>&lcub;$title&rcub;</code></dt>
54
  <dt><code>&lcub;$feed_title&rcub;</code></dt>
55
  <dt><code>&lcub;$excerpt:n&rcub;</code></dt>
 
56
  </dl>
57
  </p>
58
  </td>
@@ -64,7 +65,7 @@
64
 
65
  $value = str_replace(array('\r', '\n'), array(chr(13), chr(10)), $value);
66
 
67
- echo stripslashes($value);
68
  ?></textarea>
69
  </td>
70
  </tr>
@@ -273,7 +274,6 @@
273
  ?>
274
  <input type="submit" value="Export your Feeds and Setting as CSV File" name="csv_download" class="button button-primary button-large"<?php echo $disabled; ?> />
275
  </td>
276
-
277
  </tr>
278
  <tr>
279
  <td>
@@ -296,13 +296,44 @@ strip_html = strip html tags - "true" or "false"', "rss_pi"); ?></p>
296
  $disabled = '';
297
  if (!$this->is_key_valid) {
298
  $disabled = ' disabled="disabled"';
299
- // $this->key_error($this->key_prompt, true);
300
  $this->key_error( sprintf( $this->key_prompt, '', 'http://www.feedsapi.com/?utm_source=rsspostimporter&utm_medium=upgrade&utm_term=import-feeds&utm_content=rsspi-full-rss-key-here&utm_campaign=wordpress' ), true );
301
  }
302
  ?>
303
  <input type="file" name="import_csv"<?php echo $disabled; ?> />
304
  </td>
305
  </tr>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
306
  </table>
307
  </td>
308
  </tr>
53
  <dt><code>&lcub;$title&rcub;</code></dt>
54
  <dt><code>&lcub;$feed_title&rcub;</code></dt>
55
  <dt><code>&lcub;$excerpt:n&rcub;</code></dt>
56
+ <dt><code>&lcub;$inline_image&rcub;</code> <small>insert the featured image inline into the post content</small></dt>
57
  </dl>
58
  </p>
59
  </td>
65
 
66
  $value = str_replace(array('\r', '\n'), array(chr(13), chr(10)), $value);
67
 
68
+ echo esc_textarea(stripslashes($value));
69
  ?></textarea>
70
  </td>
71
  </tr>
274
  ?>
275
  <input type="submit" value="Export your Feeds and Setting as CSV File" name="csv_download" class="button button-primary button-large"<?php echo $disabled; ?> />
276
  </td>
 
277
  </tr>
278
  <tr>
279
  <td>
296
  $disabled = '';
297
  if (!$this->is_key_valid) {
298
  $disabled = ' disabled="disabled"';
 
299
  $this->key_error( sprintf( $this->key_prompt, '', 'http://www.feedsapi.com/?utm_source=rsspostimporter&utm_medium=upgrade&utm_term=import-feeds&utm_content=rsspi-full-rss-key-here&utm_campaign=wordpress' ), true );
300
  }
301
  ?>
302
  <input type="file" name="import_csv"<?php echo $disabled; ?> />
303
  </td>
304
  </tr>
305
+ <tr>
306
+ <td>
307
+ <?php _e('Export and backup your Feeds as OPML File', "rss_pi"); ?>
308
+ <p class="description"><?php _e('This option will help you download an OPML file with all your feeds so you can upload it back later.', "rss_pi"); ?></p>
309
+ </td>
310
+ <td>
311
+ <?php
312
+ $disabled = '';
313
+ if (!$this->is_key_valid) {
314
+ $disabled = ' disabled="disabled"';
315
+ $this->key_error( sprintf( $this->key_prompt, '', 'http://www.feedsapi.com/?utm_source=rsspostimporter&utm_medium=upgrade&utm_term=export-opml&utm_content=rsspi-full-rss-key-here&utm_campaign=wordpress' ), true );
316
+ }
317
+ ?>
318
+ <input type="submit" value="Export your Feeds as OPML File" name="export_opml" class="button button-primary button-large"<?php echo $disabled; ?> />
319
+ </td>
320
+ </tr>
321
+ <tr>
322
+ <td>
323
+ <?php _e('Import your OPML file with your feeds', "rss_pi"); ?>
324
+ <p class="description"><?php _e('Create and Import an OPML file with your Feeds', "rss_pi"); ?></p>
325
+ </td>
326
+ <td>
327
+ <?php
328
+ $disabled = '';
329
+ if (!$this->is_key_valid) {
330
+ $disabled = ' disabled="disabled"';
331
+ $this->key_error( sprintf( $this->key_prompt, '', 'http://www.feedsapi.com/?utm_source=rsspostimporter&utm_medium=upgrade&utm_term=import-opml&utm_content=rsspi-full-rss-key-here&utm_campaign=wordpress' ), true );
332
+ }
333
+ ?>
334
+ <input type="file" name="import_opml"<?php echo $disabled; ?> />
335
+ </td>
336
+ </tr>
337
  </table>
338
  </td>
339
  </tr>
index.php CHANGED
@@ -5,7 +5,7 @@
5
  Plugin URI: https://wordpress.org/plugins/rss-post-importer/
6
  Description: This plugin lets you set up an import posts from one or several rss-feeds and save them as posts on your site, simple and flexible.
7
  Author: feedsapi
8
- Version: 2.1.2
9
  Author URI: https://www.feedsapi.org/
10
  License: GPLv2 or later
11
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
@@ -27,7 +27,7 @@ if (!defined('RSS_PI_BASENAME')) {
27
  }
28
 
29
  if (!defined('RSS_PI_VERSION')) {
30
- define('RSS_PI_VERSION', '2.1.2');
31
  }
32
 
33
  if (!defined('RSS_PI_LOG_PATH')) {
@@ -43,12 +43,14 @@ include_once RSS_PI_PATH . 'app/classes/helpers/class-rss-pi-log.php';
43
  include_once RSS_PI_PATH . 'app/classes/helpers/class-rss-pi-featured-image.php';
44
  include_once RSS_PI_PATH . 'app/classes/helpers/class-rss-pi-parser.php';
45
  include_once RSS_PI_PATH . 'app/classes/helpers/rss-pi-functions.php';
 
46
 
47
  // admin classes
48
  include_once RSS_PI_PATH . 'app/classes/admin/class-rss-pi-admin-processor.php';
49
  include_once RSS_PI_PATH . 'app/classes/admin/class-rss-pi-admin.php';
50
  include_once RSS_PI_PATH . 'app/classes/admin/class-rss-pi-export-to-csv.php';
51
  include_once RSS_PI_PATH . 'app/classes/admin/class-rss-pi-stats.php';
 
52
 
53
  // Front classes
54
  include_once RSS_PI_PATH . 'app/classes/front/class-rss-pi-front.php';
5
  Plugin URI: https://wordpress.org/plugins/rss-post-importer/
6
  Description: This plugin lets you set up an import posts from one or several rss-feeds and save them as posts on your site, simple and flexible.
7
  Author: feedsapi
8
+ Version: 2.1.3
9
  Author URI: https://www.feedsapi.org/
10
  License: GPLv2 or later
11
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
27
  }
28
 
29
  if (!defined('RSS_PI_VERSION')) {
30
+ define('RSS_PI_VERSION', '2.1.3');
31
  }
32
 
33
  if (!defined('RSS_PI_LOG_PATH')) {
43
  include_once RSS_PI_PATH . 'app/classes/helpers/class-rss-pi-featured-image.php';
44
  include_once RSS_PI_PATH . 'app/classes/helpers/class-rss-pi-parser.php';
45
  include_once RSS_PI_PATH . 'app/classes/helpers/rss-pi-functions.php';
46
+ include_once RSS_PI_PATH . 'app/classes/helpers/class-OPMLParser.php'; // OPML Parser
47
 
48
  // admin classes
49
  include_once RSS_PI_PATH . 'app/classes/admin/class-rss-pi-admin-processor.php';
50
  include_once RSS_PI_PATH . 'app/classes/admin/class-rss-pi-admin.php';
51
  include_once RSS_PI_PATH . 'app/classes/admin/class-rss-pi-export-to-csv.php';
52
  include_once RSS_PI_PATH . 'app/classes/admin/class-rss-pi-stats.php';
53
+ include_once RSS_PI_PATH . 'app/classes/admin/class-rss-pi-opml.php';
54
 
55
  // Front classes
56
  include_once RSS_PI_PATH . 'app/classes/front/class-rss-pi-front.php';
readme.txt CHANGED
@@ -3,8 +3,8 @@ Contributors: feedsapi
3
  Donate link: https://www.feedsapi.org/
4
  Tags: rss, feeds, import, feed, autoblog, feed aggregation, rss-feed, aggregation, atom, feed, rss, syndication, FeedWordPress, autoblog aggregator, Autoblogger, autoblogging, feed import, rss multi importer, feed reader, feed to post, multi feed import, multi feed importer, multi import, multi rss feeds, multiple feed import, multiple feeds, multiple rss feeds, rss, rss aggregator, rss feader, RSS import, rss to post, content curation, RSS Retriever, RSS fetch feed, WP RSS Aggregator, AutoPost, RSS Feed to Post, RSSImport, yahoo pipes, WP Pipes, Import XML feed , FeedSyndicate, RSSpost, RSS in Page
5
  Requires at least: 3.5
6
- Tested up to: 4.2.1
7
- Stable tag: 2.1.2
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
@@ -123,11 +123,15 @@ Learn more (and get detailed instructions) in our [contribute guide](http://jetp
123
 
124
  == Change Log ==
125
 
 
 
 
 
126
  = Version 2.1.2 =
127
  * Interactive Feedback when adding/editing new feeds
128
  * Disable Banner for Premium users
129
  * Option to purge deleted posts from the memory, so you can re-import deleted posts.
130
- * Import/Export Feeds + Feeds Settings as CSV file
131
  * Improved Charts and Statistics
132
  * Option to Import already imported or deleted Posts
133
  * Several code improvements & Bug fixes for better stability
3
  Donate link: https://www.feedsapi.org/
4
  Tags: rss, feeds, import, feed, autoblog, feed aggregation, rss-feed, aggregation, atom, feed, rss, syndication, FeedWordPress, autoblog aggregator, Autoblogger, autoblogging, feed import, rss multi importer, feed reader, feed to post, multi feed import, multi feed importer, multi import, multi rss feeds, multiple feed import, multiple feeds, multiple rss feeds, rss, rss aggregator, rss feader, RSS import, rss to post, content curation, RSS Retriever, RSS fetch feed, WP RSS Aggregator, AutoPost, RSS Feed to Post, RSSImport, yahoo pipes, WP Pipes, Import XML feed , FeedSyndicate, RSSpost, RSS in Page
5
  Requires at least: 3.5
6
+ Tested up to: 4.2.2
7
+ Stable tag: 2.1.3
8
  License: GPLv2 or later
9
  License URI: http://www.gnu.org/licenses/gpl-2.0.html
10
 
123
 
124
  == Change Log ==
125
 
126
+ = Version 2.1.3 =
127
+ * Template item inline_image to insert Featured image inline into post
128
+ * Import/Export Feeds + Feeds' Settings as OPML file
129
+
130
  = Version 2.1.2 =
131
  * Interactive Feedback when adding/editing new feeds
132
  * Disable Banner for Premium users
133
  * Option to purge deleted posts from the memory, so you can re-import deleted posts.
134
+ * Import/Export Feeds + Feeds' Settings as CSV file
135
  * Improved Charts and Statistics
136
  * Option to Import already imported or deleted Posts
137
  * Several code improvements & Bug fixes for better stability