Changeset 2651566
- Timestamp:
- 01/01/2022 03:07:40 PM (4 years ago)
- Location:
- planetplanet/trunk
- Files:
-
- 3 edited
-
planetplanet-cli.php (modified) (7 diffs)
-
planetplanet.php (modified) (44 diffs)
-
readme.txt (modified) (8 diffs)
Legend:
- Unmodified
- Added
- Removed
-
planetplanet/trunk/planetplanet-cli.php
r2650916 r2651566 42 42 */ 43 43 44 public function list_feeds( $args, $assoc) {44 public function list_feeds( $args, $assoc ) { 45 45 $links = get_bookmarks(); 46 46 47 if ( isset($assoc['match'])) {47 if ( isset( $assoc['match'] ) ) { 48 48 $s = $assoc['match']; 49 $links = array_filter( $links, function ($link) use ($s) {50 if ( stripos($link->link_name, $s) !== false) return true;51 if ( stripos($link->link_url, $s) !== false) return true;52 if ( stripos($link->link_rss, $s) !== false) return true;49 $links = array_filter( $links, function ( $link ) use ( $s ) { 50 if ( stripos( $link->link_name, $s ) !== false ) return true; 51 if ( stripos( $link->link_url, $s ) !== false ) return true; 52 if ( stripos( $link->link_rss, $s ) !== false ) return true; 53 53 return false; 54 } );54 } ); 55 55 } 56 56 … … 75 75 */ 76 76 77 public function update_all( $args, $assoc) {78 global $planetplanet; 79 80 if ( isset($assoc['log-level']))81 $planetplanet->set_loglevel( $assoc['log-level']);77 public function update_all( $args, $assoc ) { 78 global $planetplanet; 79 80 if ( isset( $assoc['log-level'] ) ) 81 $planetplanet->set_loglevel( $assoc['log-level'] ); 82 82 83 83 $planetplanet->update_all_feeds(); 84 WP_CLI::success( 'done.');84 WP_CLI::success( __('Done.', 'planetplanet') ); 85 85 } 86 86 … … 107 107 */ 108 108 109 public function update($args, $assoc) { 110 global $planetplanet; 111 112 if (isset($assoc['log-level'])) 113 $planetplanet->set_loglevel($assoc['log-level']); 114 115 foreach ($args as $arg) { 116 $link = get_bookmark((int)$arg); 117 if (isset($link)) { 118 $planetplanet->update_feed($link); 119 WP_CLI::success( sprintf( 'Updated feed %d %s', $link->link_id, $link->link_name) ); 120 } else 121 WP_CLI::warning( sprintf( 'No feed with ID %s', $arg) ); 109 public function update( $args, $assoc ) { 110 global $planetplanet; 111 112 if ( isset( $assoc['log-level'] ) ) 113 $planetplanet->set_loglevel( $assoc['log-level']); 114 115 foreach ( $args as $arg ) { 116 $link = get_bookmark( (int)$arg ); 117 if ( isset( $link ) ) { 118 $planetplanet->update_feed( $link ); 119 WP_CLI::success( sprintf( __('Updated feed %d %s', 'planetplanet'), $link->link_id, $link->link_name) ); 120 } else { 121 WP_CLI::warning( sprintf( __('No feed with ID %s', 'planetplanet'), $arg) ); 122 } 122 123 } 123 124 } … … 140 141 */ 141 142 142 public function purge( $args, $assoc) {143 global $planetplanet; 144 145 if ( isset($assoc['log-level']))146 $planetplanet->set_loglevel( $assoc['log-level']);143 public function purge( $args, $assoc ) { 144 global $planetplanet; 145 146 if ( isset( $assoc['log-level'] ) ) 147 $planetplanet->set_loglevel( $assoc['log-level'] ); 147 148 148 149 $planetplanet->purge_posts(); 149 WP_CLI::success( 'done.');150 WP_CLI::success( __('Done.', 'planetplanet') ); 150 151 } 151 152 … … 172 173 * 173 174 */ 174 function scan( $args, $assoc) {175 function scan( $args, $assoc ) { 175 176 global $planetplanet; 176 177 177 178 $site = $args[0]; 178 179 179 $response = $planetplanet->get_web_page( $site);180 if ( is_wp_error($response))181 WP_CLI::error( $response);180 $response = $planetplanet->get_web_page( $site ); 181 if ( is_wp_error( $response ) ) 182 WP_CLI::error( $response ); 182 183 183 184 $feed_links = []; 184 185 185 $input = mb_convert_encoding( $response['body'], 'HTML-ENTITIES', 'UTF-8');186 $xml = new DOMDocument( '1.0', 'UTF-8');187 $error_mode = libxml_use_internal_errors( true);188 189 if ($xml->LoadHTML( $input, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD)) {190 $links = $xml->getElementsByTagName( 'link');191 192 foreach ( $links as $link) {186 $input = mb_convert_encoding( $response['body'], 'HTML-ENTITIES', 'UTF-8' ); 187 $xml = new DOMDocument( '1.0', 'UTF-8' ); 188 $error_mode = libxml_use_internal_errors( true ); 189 190 if ($xml->LoadHTML( $input, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD )) { 191 $links = $xml->getElementsByTagName( 'link' ); 192 193 foreach ( $links as $link ) { 193 194 $attrs = $link->attributes; 194 195 195 196 // printf("LINK %s\n", $link->ownerDocument->saveXML($link)); 196 197 197 $attr = $attrs->getNamedItem( 'rel');198 if ( !isset($attr) or $attr->value != 'alternate') continue;199 200 $attr = $attrs->getNamedItem( 'type');201 if ( !isset($attr) or $attr->value != 'application/rss+xml') continue;202 203 $attr = $attrs->getNamedItem( 'href');204 if ( !isset($attr) or empty($attr->value)) continue;198 $attr = $attrs->getNamedItem( 'rel' ); 199 if ( !isset( $attr ) or $attr->value != 'alternate' ) continue; 200 201 $attr = $attrs->getNamedItem( 'type' ); 202 if ( !isset( $attr ) or $attr->value != 'application/rss+xml' ) continue; 203 204 $attr = $attrs->getNamedItem( 'href' ); 205 if ( !isset( $attr ) or empty( $attr->value ) ) continue; 205 206 $href = $attr->value; 206 207 207 $attr = $attrs->getNamedItem( 'title');208 if ( !isset($attr) or empty($attr->value)) continue;208 $attr = $attrs->getNamedItem( 'title' ); 209 if ( !isset( $attr ) or empty( $attr->value ) ) continue; 209 210 $title = $attr->value; 210 211 211 $feed_links[] = [ 'Title' => $title, 'Feed url'=> $href ];212 $feed_links[] = [ __('Title', 'planetplanet') => $title, __('Feed url', 'planetplanet') => $href ]; 212 213 } 213 214 } 214 libxml_use_internal_errors($error_mode); 215 216 if ($feed_links) { 217 WP_CLI\Utils\format_items( $assoc['format'], $feed_links, 'Title,Feed url' ); 218 $nfeeds = count($feed_links); 219 WP_CLI::success("Found {$nfeeds} feeds."); 215 libxml_use_internal_errors( $error_mode ); 216 217 if ( $feed_links ) { 218 WP_CLI\Utils\format_items( $assoc['format'], $feed_links, join(',', array_keys($feed_links[0])) ); 219 WP_CLI::success( sprintf( __('Found %d feeds.', 'planetplanet'), count( $feed_links )) ); 220 } else { 221 WP_CLI::warning(__('No feeds found.', 'planetplanet')); 220 222 } 221 223 } … … 231 233 */ 232 234 233 function add( $args, $assoc) {235 function add( $args, $assoc ) { 234 236 global $planetplanet; 235 237 236 238 $url = $args[0]; 237 239 238 if ( $planetplanet->find_feed($url))239 WP_CLI::error( "Feed already configured: $url");240 241 $response = $planetplanet->get_web_page( $url);242 if ( is_wp_error($response))243 WP_CLI::error( $response);244 245 $data = $planetplanet->parse_feed( $response['body']);246 if ( is_wp_error($data))247 WP_CLI::error( $data);248 249 if ( isset($data['link_rss']) and $data['link_rss'] != $url) {250 if ( $planetplanet->find_feed($data['link_rss']))251 WP_CLI::error("Feed already configured: {$data['link_rss']}");240 if ( $planetplanet->find_feed( $url ) ) 241 WP_CLI::error( sprintf( __("Feed already configured: %s", 'planetplanet'), $url ) ); 242 243 $response = $planetplanet->get_web_page( $url ); 244 if ( is_wp_error( $response ) ) 245 WP_CLI::error( $response ); 246 247 $data = $planetplanet->parse_feed( $response['body'] ); 248 if ( is_wp_error( $data ) ) 249 WP_CLI::error( $data ); 250 251 if ( isset( $data['link_rss'] ) and $data['link_rss'] != $url ) { 252 if ( $planetplanet->find_feed( $data['link_rss'] ) ) 253 WP_CLI::error( sprintf( __("Feed already configured: %s", 'planetplanet'), $data['link_rss'] ) ); 252 254 } 253 255 … … 255 257 'link_id' => 0, 256 258 'link_url' => $data['link'], 257 'link_rss' => ( !empty($data['link_rss']) ? $data['link_rss'] : $url),259 'link_rss' => ( !empty( $data['link_rss'] ) ? $data['link_rss'] : $url ), 258 260 'link_name' => $data['title'], 259 261 ]; 260 262 261 WP_CLI::log( "Site name: {$link['link_name']}");262 WP_CLI::log( "Site url: {$link['link_url']}");263 WP_CLI::log( "Feed url: {$link['link_rss']}");263 WP_CLI::log( sprintf( __("Site name: %s", 'planetplanet'), $link['link_name'] ) ); 264 WP_CLI::log( sprintf( __("Site url: %s", 'planetplanet'), $link['link_url'] ) ); 265 WP_CLI::log( sprintf( __("Feed url: %s", 'planetplanet' ), $link['link_rss'] ) ); 264 266 265 267 return; 266 268 267 $id = wp_insert_link( $link, true);268 if ( is_wp_error($id))269 WP_CLI::error( $id);270 271 WP_CLI::success( "Link added");269 $id = wp_insert_link( $link, true ); 270 if ( is_wp_error( $id ) ) 271 WP_CLI::error( $id ); 272 273 WP_CLI::success( __("Link added", 'planetplanet' ) ); 272 274 } 273 275 } -
planetplanet/trunk/planetplanet.php
r2650916 r2651566 13 13 */ 14 14 15 require_once( ABSPATH . 'wp-admin/includes/taxonomy.php');16 require_once( ABSPATH . 'wp-admin/includes/bookmark.php');15 require_once( ABSPATH . 'wp-admin/includes/taxonomy.php' ); 16 require_once( ABSPATH . 'wp-admin/includes/bookmark.php' ); 17 17 // for media upload metadata 18 require_once( ABSPATH . 'wp-admin/includes/image.php');19 require_once( ABSPATH . 'wp-admin/includes/media.php');20 21 require_once( __DIR__ . '/logger.php');18 require_once( ABSPATH . 'wp-admin/includes/image.php' ); 19 require_once( ABSPATH . 'wp-admin/includes/media.php' ); 20 21 require_once( __DIR__ . '/logger.php' ); 22 22 23 23 class PlanetPlanetPlugin { … … 26 26 27 27 function __construct() { 28 $this->is_cli = ( PHP_SAPI == 'cli');28 $this->is_cli = ( PHP_SAPI == 'cli' ); 29 29 30 30 $this->set_loglevel(); 31 31 32 32 // Activate the Links section in WP 33 add_filter( 'pre_option_link_manager_enabled', '__return_true');33 add_filter( 'pre_option_link_manager_enabled', '__return_true' ); 34 34 35 35 // Automatick setup of ajax handlers, actions, filters and shortcodes 36 36 // based on available class methods 37 $methods = get_class_methods( $this);38 foreach ( $methods as $m) {39 if ( preg_match('/^ajax_(.*)_handler$/', $m, $match)) {40 add_action( "wp_ajax_{$match[1]}", [$this, "ajax_{$match[1]}_handler"]);41 } 42 elseif ( preg_match('/^do_(.*)_shortcode$/', $m, $match)) {43 add_shortcode( $match[1], [$this, "do_{$match[1]}_shortcode"]);44 } 45 elseif ( preg_match('/^do_(.*)_action$/', $m, $match)) {46 add_action( $match[1], [$this, "do_{$match[1]}_action"]);47 } 48 elseif ( preg_match('/^do_(.*)_filter$/', $m, $match)) {49 add_filter( $match[1], [$this, "do_{$match[1]}_filter"]);37 $methods = get_class_methods( $this ); 38 foreach ( $methods as $m ) { 39 if ( preg_match( '/^ajax_(.*)_handler$/', $m, $match ) ) { 40 add_action( "wp_ajax_{$match[1]}", [ $this, "ajax_{$match[1]}_handler" ] ); 41 } 42 elseif ( preg_match( '/^do_(.*)_shortcode$/', $m, $match ) ) { 43 add_shortcode( $match[1], [ $this, "do_{$match[1]}_shortcode" ] ); 44 } 45 elseif ( preg_match( '/^do_(.*)_action$/', $m, $match ) ) { 46 add_action( $match[1], [ $this, "do_{$match[1]}_action" ] ); 47 } 48 elseif ( preg_match( '/^do_(.*)_filter$/', $m, $match ) ) { 49 add_filter( $match[1], [ $this, "do_{$match[1]}_filter" ] ); 50 50 } 51 51 } 52 52 } 53 53 54 public function set_loglevel( $level = NULL) {54 public function set_loglevel( $level = NULL ) { 55 55 $this->logger = new \PlanetPlanet\PolyLogger(); 56 56 57 $loglevel = $level ?? $this->get_option( 'loglevel');58 59 $debug = ( $loglevel == 'debug');60 $silent = ( $loglevel == 'errors');57 $loglevel = $level ?? $this->get_option( 'loglevel' ); 58 59 $debug = ( $loglevel == 'debug' ); 60 $silent = ( $loglevel == 'errors' ); 61 61 62 62 $upload = wp_get_upload_dir(); 63 $logfile = sprintf( '%s/%s-%s.log', $upload['basedir'], __CLASS__, date('Y-m-d'));64 if ( !$this->is_cli or is_writable($logfile))65 $this->logger->add( new \PlanetPlanet\FileLogger($logfile, true));66 67 if ( $this->is_cli)68 $this->logger->add( new \PlanetPlanet\CLILogger($debug, $silent));69 elseif ( $this->has_option('email'))70 $this->logger->add( new \PlanetPlanet\MailLogger($this->get_option('email'), __CLASS__, $debug, $silent));63 $logfile = sprintf( '%s/%s-%s.log', $upload['basedir'], __CLASS__, date( 'Y-m-d' ) ); 64 if ( !$this->is_cli or is_writable( $logfile ) ) 65 $this->logger->add( new \PlanetPlanet\FileLogger( $logfile, true ) ); 66 67 if ( $this->is_cli ) 68 $this->logger->add( new \PlanetPlanet\CLILogger( $debug, $silent ) ); 69 elseif ( $this->has_option( 'email' ) ) 70 $this->logger->add( new \PlanetPlanet\MailLogger( $this->get_option( 'email' ), __CLASS__, $debug, $silent ) ); 71 71 } 72 72 … … 78 78 ************************************************************************/ 79 79 80 function safe_datetime( $time) {80 function safe_datetime( $time ) { 81 81 try { 82 return new DateTime( $time);83 } catch ( Exception $e) {82 return new DateTime( $time ); 83 } catch ( Exception $e ) { 84 84 return NULL; 85 85 } … … 89 89 // - return response or WP_Error 90 90 91 function get_web_page( $url) {91 function get_web_page( $url ) { 92 92 $args = [ 93 93 'sslverify' => false, 94 94 ]; 95 95 96 if ( $this->has_option('timeout'))97 $args['timeout'] = $this->get_option( 'timeout');98 99 if ( $this->has_option('user_agent'))100 $args['user-agent'] = $this->get_option( 'user_agent');101 102 $response = ( new WP_Http())->get($url, $args);103 104 if ( is_wp_error($response))96 if ( $this->has_option( 'timeout' ) ) 97 $args['timeout'] = $this->get_option( 'timeout' ); 98 99 if ( $this->has_option( 'user_agent' ) ) 100 $args['user-agent'] = $this->get_option( 'user_agent' ); 101 102 $response = ( new WP_Http() )->get( $url, $args ); 103 104 if ( is_wp_error( $response ) ) 105 105 return $response; 106 106 107 if ($response['response']['code'] != WP_Http::OK) 108 return new WP_Error('http_error', 109 sprintf('HTTP response %d %s', $response['response']['code'], $response['response']['message'])); 107 if ( $response['response']['code'] != WP_Http::OK ) 108 return new WP_Error( 'http_error', 109 sprintf( __('HTTP response %d %s', 'planetplanet'), 110 $response['response']['code'], 111 $response['response']['message'] 112 ) 113 ); 110 114 111 115 return $response; … … 118 122 ************************************************************************/ 119 123 120 function get_option( $name) {121 $options = get_option( 'planetplanet_options');122 if ( array_key_exists($name, $options)) return $options[$name];123 if ( array_key_exists("planetplanet_$name", $options)) return $options["planetplanet_$name"];124 function get_option( $name ) { 125 $options = get_option( 'planetplanet_options' ); 126 if ( array_key_exists( $name, $options ) ) return $options[$name]; 127 if ( array_key_exists( "planetplanet_$name", $options ) ) return $options["planetplanet_$name"]; 124 128 return NULL; 125 129 } 126 function has_option( $name) {127 return !empty( $this->get_option($name));130 function has_option( $name ) { 131 return !empty( $this->get_option( $name ) ); 128 132 } 129 133 … … 144 148 'manage_options', 145 149 'planetplanet', 146 [ $this, 'planetplanet_options_page_html']150 [ $this, 'planetplanet_options_page_html' ] 147 151 ); 148 152 } … … 158 162 // wordpress will add the "settings-updated" $_GET parameter to the url 159 163 if ( isset( $_GET['settings-updated'] ) ) { 160 add_settings_error( 'planetplanet', 'settings_saved', __( 'Settings Saved', 'planetplanet' ), 'updated');164 add_settings_error( 'planetplanet', 'settings_saved', __( 'Settings Saved', 'planetplanet' ), 'updated' ); 161 165 } 162 166 … … 164 168 // settings_errors('planetplanet'); 165 169 166 print( '<div class="wrap">');167 printf( '<h1>%s</h1>', esc_html(get_admin_page_title()));168 print( '<form action="options.php" method="post">');169 170 settings_fields( 'planetplanet');171 do_settings_sections( 'planetplanet');170 print( '<div class="wrap">' ); 171 printf( '<h1>%s</h1>', esc_html( get_admin_page_title() ) ); 172 print( '<form action="options.php" method="post">' ); 173 174 settings_fields( 'planetplanet' ); 175 do_settings_sections( 'planetplanet' ); 172 176 submit_button( 'Save Settings' ); 173 177 174 print( '</form>');175 print( '</div>');178 print( '</form>' ); 179 print( '</div>' ); 176 180 } 177 181 … … 181 185 function do_admin_init_action() { 182 186 // register a new setting for "planetplanet" page 183 register_setting('planetplanet', 'planetplanet_options', 184 [ 'sanitize_callback' => [ $this, 'planetplanet_options_validate' ]], 185 [ 186 'planetplanet_schedule' => 'hourly', 187 'planetplanet_retain_limit' => '1 year ago', 188 'planetplanet_email' => '', 189 'planetplanet_loglevel' => 'errors', 190 ], 187 register_setting( 188 'planetplanet', 'planetplanet_options', 189 [ 'sanitize_callback' => [ $this, 'planetplanet_options_validate' ]], 190 [ 191 'planetplanet_schedule' => 'hourly', 192 'planetplanet_retain_limit' => '1 year ago', 193 'planetplanet_email' => '', 194 'planetplanet_loglevel' => 'errors', 195 ], 191 196 ); 192 197 … … 195 200 'planetplanet_schedule_section', 196 201 __( 'Update schedule and retention', 'planetplanet' ), 197 [ $this, 'planetplanet_settings_html'],202 [ $this, 'planetplanet_settings_html' ], 198 203 'planetplanet' 199 204 ); 200 205 201 206 $schedule_menu = [ 'none' => __('None', 'planetplanet') ]; 202 foreach ( wp_get_schedules() as $k => $v)207 foreach ( wp_get_schedules() as $k => $v ) 203 208 $schedule_menu[$k] = $v['display']; 204 209 … … 229 234 'label_for' => 'planetplanet_retain_limit', 230 235 'settings' => 'planetplanet_options', 231 'help' => 'e.g., "1 year ago", "-6 months", "72 days ago"',236 'help' => __('e.g., "1 year ago", "-6 months", "72 days ago"', 'planetplanet'), 232 237 ] 233 238 ); … … 257 262 ], 258 263 259 'help' => 'Feeds can always be reactivated in the Links manager',264 'help' => __('Feeds can always be reactivated in the Links manager', 'planetplanet'), 260 265 ] 261 266 ); … … 280 285 281 286 'validator' => 'is_email', 282 'error' => 'Invalid email address will be ignored',287 'error' => __('Invalid email address will be ignored', 'planetplanet'), 283 288 ] 284 289 ); … … 297 302 'default' => 'errors', 298 303 'menu' => [ 299 'errors' => 'Only error messages',300 'messages' => 'Errors and updates',301 'debug' => 'Everything',304 'errors' => __('Only error messages', 'planetplanet'), 305 'messages' => __('Errors and updates', 'planetplanet'), 306 'debug' => __('Everything', 'planetplanet'), 302 307 ], 303 308 ] … … 311 316 'planetplanet_network_section', 312 317 __( 'Network', 'planetplanet' ), 313 [ $this, 'planetplanet_settings_html'],318 [ $this, 'planetplanet_settings_html' ], 314 319 'planetplanet' 315 320 ); … … 318 323 'timeout', 319 324 __( 'Timeout for feed requests', 'planetplanet' ), 320 [ $this, 'planetplanet_options_simple_menu_html'],325 [ $this, 'planetplanet_options_simple_menu_html' ], 321 326 'planetplanet', 322 327 'planetplanet_network_section', … … 339 344 'user_agent', 340 345 __( 'User-Agent', 'planetplanet' ), 341 [ $this, 'planetplanet_options_simple_input_html'],346 [ $this, 'planetplanet_options_simple_input_html' ], 342 347 'planetplanet', 343 348 'planetplanet_network_section', … … 348 353 ] 349 354 ); 350 351 352 355 } 353 356 … … 357 360 */ 358 361 359 function planetplanet_options_validate( $input = NULl) {360 if ( !empty($input['planetplanet_email']) and !is_email($input['planetplanet_email']))361 add_settings_error( 'planetplanet', 'invalid_email', __('Invalid email address for updates', 'planetplanet'));362 363 if ( isset($input['planetplanet_retain_limit'])) {364 if ( !$this->safe_datetime($input['planetplanet_retain_limit'])) {365 add_settings_error( 'planetplanet', 'invalid_date', __('Invalid date value for discard limit', 'planetplanet'));362 function planetplanet_options_validate( $input = NULl ) { 363 if ( !empty( $input['planetplanet_email'] ) and !is_email( $input['planetplanet_email'] ) ) 364 add_settings_error( 'planetplanet', 'invalid_email', __( 'Invalid email address for updates', 'planetplanet' ) ); 365 366 if ( isset( $input['planetplanet_retain_limit'] ) ) { 367 if ( !$this->safe_datetime( $input['planetplanet_retain_limit'] ) ) { 368 add_settings_error( 'planetplanet', 'invalid_date', __( 'Invalid date value for discard limit', 'planetplanet' ) ); 366 369 $input['planetplanet_retain_limit'] = NULL; 367 370 } … … 371 374 } 372 375 373 function planetplanet_settings_html( $args) {374 } 375 376 function planetplanet_options_simple_menu_html( $args) {377 $value = $this->get_option( $args['field']) ?? $args['default'];378 379 printf( '<select id="%s" name="%s[%s]">',380 esc_attr($args['field']),381 esc_attr($args['settings']),382 esc_attr($args['field']),383 ); 384 foreach ( $args['menu'] as $k => $v)385 printf( '<option value="%s" %s>%s</option>',386 esc_attr($k),387 ($value == $k ? 'selected' : ''),388 esc_html($v)376 function planetplanet_settings_html( $args ) { 377 } 378 379 function planetplanet_options_simple_menu_html( $args ) { 380 $value = $this->get_option( $args['field'] ) ?? $args['default']; 381 382 printf( '<select id="%s" name="%s[%s]">', 383 esc_attr( $args['field'] ), 384 esc_attr( $args['settings'] ), 385 esc_attr( $args['field'] ), 386 ); 387 foreach ( $args['menu'] as $k => $v ) 388 printf( '<option value="%s" %s>%s</option>', 389 esc_attr( $k ), 390 ( $value == $k ? 'selected' : '' ), 391 esc_html( $v ) 389 392 ); 390 printf( '</select>');391 392 if ( isset($args['help'])) {393 printf( '<p>%s</p>', $args['help']);394 } 395 } 396 397 function planetplanet_options_simple_input_html( $args) {398 $options = get_option( 'planetplanet_options');399 $value = isset( $options[$args['field']]) ? $options[$args['field']] : '';400 printf( '<input type="text" id="%s" name="%s[%s]" value="%s">',401 esc_attr( $args['field']),402 esc_attr( $args['settings']),403 esc_attr( $args['field']),404 esc_attr( $value)405 );406 407 if ( isset($args['validator']) and is_callable($args['validator']) and !empty($value) and !empty($args['error'])) {408 $valid = call_user_func( $args['validator'], $value);409 if ( !$valid)410 printf( '<div style="color:red">%s</div>', $args['error']);411 } 412 if ( isset($args['help'])) {413 printf( '<div>%s</div>', $args['help']);393 printf( '</select>' ); 394 395 if ( isset( $args['help'] ) ) { 396 printf( '<p>%s</p>', $args['help'] ); 397 } 398 } 399 400 function planetplanet_options_simple_input_html( $args ) { 401 $options = get_option( 'planetplanet_options' ); 402 $value = isset( $options[$args['field']] ) ? $options[$args['field']] : ''; 403 printf( '<input type="text" id="%s" name="%s[%s]" value="%s">', 404 esc_attr( $args['field'] ), 405 esc_attr( $args['settings'] ), 406 esc_attr( $args['field'] ), 407 esc_attr( $value ) 408 ); 409 410 if ( isset( $args['validator'] ) and is_callable( $args['validator'] ) and !empty( $value ) and !empty( $args['error'] ) ) { 411 $valid = call_user_func( $args['validator'], $value ); 412 if ( !$valid ) 413 printf( '<div style="color:red">%s</div>', $args['error'] ); 414 } 415 if ( isset( $args['help'] ) ) { 416 printf( '<div>%s</div>', $args['help'] ); 414 417 } 415 418 } … … 423 426 ************************************************************************/ 424 427 425 function redirect( $url) {426 wp_redirect( $url);428 function redirect( $url ) { 429 wp_redirect( $url ); 427 430 die; 428 431 } … … 432 435 global $post; 433 436 434 if ( !$post) return;435 436 if ( is_single()) {437 if ( $target = $post->item_link) {437 if ( !$post ) return; 438 439 if ( is_single() ) { 440 if ( $target = $post->item_link ) { 438 441 # If it looks like an URL go there 439 if ( strpos($target, 'https://') === 0 || strpos($target, 'http://') === 0) {440 $this->redirect( $target);442 if ( strpos( $target, 'https://' ) === 0 || strpos( $target, 'http://' ) === 0 ) { 443 $this->redirect( $target ); 441 444 } 442 445 } 443 446 } 444 elseif ( is_category()) {447 elseif ( is_category() ) { 445 448 $cat = get_queried_object(); 446 $target = $this->get_category_link( $cat);447 if ( $target)448 $this->redirect( $target);449 $target = $this->get_category_link( $cat ); 450 if ( $target ) 451 $this->redirect( $target ); 449 452 } 450 453 } … … 465 468 466 469 function do_init_action() { 467 register_deactivation_hook( __FILE__, function () {468 wp_clear_scheduled_hook( 'pp_update_feeds');469 wp_clear_scheduled_hook( 'pp_purge_posts');470 } );471 472 $schedule = $this->get_option( 'schedule');473 if ( isset($schedule) and $schedule == 'none')470 register_deactivation_hook( __FILE__, function () { 471 wp_clear_scheduled_hook( 'pp_update_feeds' ); 472 wp_clear_scheduled_hook( 'pp_purge_posts' ); 473 } ); 474 475 $schedule = $this->get_option( 'schedule' ); 476 if ( isset( $schedule ) and $schedule == 'none' ) 474 477 $schedule = NULL; 475 478 476 if ( $schedule) {479 if ( $schedule ) { 477 480 $schedules = wp_get_schedules(); 478 if ( !isset($schedules[$schedule]))481 if ( !isset( $schedules[$schedule] ) ) 479 482 $schedule = NULL; 480 483 } 481 484 482 485 // Next scheduled event 483 $event = wp_get_scheduled_event( 'pp_update_feeds');484 485 if ( $schedule) {486 if ( $event === false) {486 $event = wp_get_scheduled_event( 'pp_update_feeds' ); 487 488 if ( $schedule ) { 489 if ( $event === false ) { 487 490 // Not scheduled but should be 488 wp_schedule_event( time(), $schedule, 'pp_update_feeds');489 } 490 elseif ( $event->schedule != $schedule) {491 wp_schedule_event( time(), $schedule, 'pp_update_feeds' ); 492 } 493 elseif ( $event->schedule != $schedule ) { 491 494 // Scheduled but not correctly 492 wp_clear_scheduled_hook( 'pp_update_feeds');495 wp_clear_scheduled_hook( 'pp_update_feeds' ); 493 496 494 497 $when = $event->timestamp; 495 if ( $when > time() + $schedules[$schedule]['interval'])498 if ( $when > time() + $schedules[$schedule]['interval'] ) 496 499 $when = time() + $schedules[$schedule]['interval']; 497 500 498 wp_schedule_event( $when, $schedule, 'pp_update_feeds');501 wp_schedule_event( $when, $schedule, 'pp_update_feeds' ); 499 502 } 500 503 } else { 501 504 // Shouldn't be scheduled 502 if ( $event)503 wp_clear_scheduled_hook( 'pp_update_feeds');505 if ( $event ) 506 wp_clear_scheduled_hook( 'pp_update_feeds' ); 504 507 } 505 508 506 509 // Do the purge handler too 507 if ( ! wp_next_scheduled('pp_purge_posts')) {508 wp_schedule_event( time() + WEEK_IN_SECONDS, 'weekly', 'pp_purge_posts');510 if ( !wp_next_scheduled( 'pp_purge_posts' ) ) { 511 wp_schedule_event( time() + WEEK_IN_SECONDS, 'weekly', 'pp_purge_posts' ); 509 512 } 510 513 } … … 518 521 ************************************************************************/ 519 522 520 function parse_feed( $feed) {521 $error_mode = libxml_use_internal_errors( true);523 function parse_feed( $feed ) { 524 $error_mode = libxml_use_internal_errors( true ); 522 525 try { 523 $xml = new SimpleXMLElement( $feed);524 } catch ( Exception $e) {526 $xml = new SimpleXMLElement( $feed ); 527 } catch ( Exception $e ) { 525 528 $xml = NULL; 526 529 } 527 libxml_use_internal_errors( $error_mode);528 529 if ( empty($xml)) {530 return new WP_Error( 'xml_parse_error', "Feed is not XML");530 libxml_use_internal_errors( $error_mode ); 531 532 if ( empty( $xml ) ) { 533 return new WP_Error( 'xml_parse_error', "Feed is not XML" ); 531 534 } 532 535 533 536 $ns = $xml->GetDocNamespaces(); 534 537 535 if ( $xml->getName() == "rss") {538 if ( $xml->getName() == "rss" ) { 536 539 $output = [ 537 540 'title' => (string)$xml->channel->title, … … 540 543 ]; 541 544 542 if ( empty($output['title']))545 if ( empty( $output['title'] ) ) 543 546 $output['title'] = $output['link']; 544 547 545 if ( isset($ns['atom']))546 $feed_url = $xml->channel->children( $ns['atom'])->link;547 548 if ( isset($feed_url) and count($feed_url)>0)549 if ( (string)$feed_url->attributes()->type == 'application/rss+xml')548 if ( isset( $ns['atom'] ) ) 549 $feed_url = $xml->channel->children( $ns['atom'] )->link; 550 551 if ( isset( $feed_url ) and count( $feed_url ) > 0 ) 552 if ( (string)$feed_url->attributes()->type == 'application/rss+xml' ) 550 553 $output['link_rss'] = (string)$feed_url->attributes()->href; 551 554 552 555 $output['posts'] = []; 553 foreach ( $xml->channel->item as $item) {554 if ( isset($ns['dc']))555 $author = (string)$item->children( $ns['dc'])->creator;556 if ( empty($author))556 foreach ( $xml->channel->item as $item ) { 557 if ( isset( $ns['dc'] ) ) 558 $author = (string)$item->children( $ns['dc'] )->creator; 559 if ( empty( $author ) ) 557 560 $author = (string)$item->author; 558 561 559 $post_date = DateTime::createFromFormat( DateTime::RSS, (string)$item->pubDate);560 if ( !$post_date) {562 $post_date = DateTime::createFromFormat( DateTime::RSS, (string)$item->pubDate ); 563 if ( !$post_date ) { 561 564 continue; 562 565 } 563 566 564 if ( isset($ns['content']))565 $post_content = (string)$item->children( $ns['content'])->encoded;567 if ( isset($ns['content']) ) 568 $post_content = (string)$item->children( $ns['content'] )->encoded; 566 569 567 570 $post = [ 568 571 'post_title' => (string)$item->title, 569 'post_date' => $post_date->format( 'Y-m-d H:i:s'),570 571 'post_excerpt' => strip_tags( (string)$item->description),572 'post_date' => $post_date->format( 'Y-m-d H:i:s' ), 573 574 'post_excerpt' => strip_tags( (string)$item->description ), 572 575 'post_content' => $post_content ?? '', 573 576 574 'guid' => site_url( (string)$item->guid, 'https'),577 'guid' => site_url( (string)$item->guid, 'https' ), 575 578 576 579 'meta_input' => [ … … 584 587 } 585 588 } 586 elseif ( $xml->getName() == "feed") {589 elseif ( $xml->getName() == "feed" ) { 587 590 $output = [ 588 591 'title' => (string)$xml->title, … … 590 593 ]; 591 594 592 foreach ( $xml->link as $link) {593 if ( $link->attributes()->rel == "alternate" and $link->attributes()->type == "text/html") {595 foreach ( $xml->link as $link ) { 596 if ( $link->attributes()->rel == "alternate" and $link->attributes()->type == "text/html" ) { 594 597 $output['link'] = (string)$link->attributes()->href; 595 598 break; … … 597 600 } 598 601 599 if ( empty($output['title']))602 if ( empty( $output['title'] ) ) 600 603 $output['title'] = $output['link']; 601 604 602 605 $output['posts'] = []; 603 foreach ( $xml->entry as $item) {606 foreach ( $xml->entry as $item ) { 604 607 $item_link = NULL; 605 foreach ( $item->link as $link) {606 if ( $link->attributes()->rel == "alternate" and $link->attributes()->type == "text/html") {608 foreach ( $item->link as $link ) { 609 if ( $link->attributes()->rel == "alternate" and $link->attributes()->type == "text/html" ) { 607 610 $item_link = (string)$link->attributes()->href; 608 611 break; 609 612 } 610 613 } 611 $post_date = DateTime::createFromFormat( DateTime::RFC3339, (string)$item->published);612 if ( !$post_date)613 $post_date = DateTime::createFromFormat( DateTime::RFC3339_EXTENDED, (string)$item->published);614 if ( !$post_date)614 $post_date = DateTime::createFromFormat( DateTime::RFC3339, (string)$item->published ); 615 if ( !$post_date ) 616 $post_date = DateTime::createFromFormat( DateTime::RFC3339_EXTENDED, (string)$item->published ); 617 if ( !$post_date ) 615 618 continue; // Skip if we can't understand the date 616 619 617 620 $post = [ 618 621 'post_title' => (string)$item->title, 619 'post_date' => $post_date->format( 'Y-m-d H:i:s'),622 'post_date' => $post_date->format( 'Y-m-d H:i:s' ), 620 623 621 624 'post_excerpt' => (string)$item->summary, 622 625 'post_content' => (string)$item->content, 623 626 624 'guid' => site_url( (string)$item->id, 'https'),627 'guid' => site_url( (string)$item->id, 'https' ), 625 628 626 629 'meta_input' => [ … … 631 634 ]; 632 635 633 // print_r( $post);636 // print_r( $post ); 634 637 635 638 $output['posts'][] = $post; … … 640 643 } 641 644 642 foreach ( $output['posts'] as &$post) {645 foreach ( $output['posts'] as &$post ) { 643 646 $post['ID'] = 0; 644 647 $post['post_status'] = 'publish'; 645 648 646 if ( empty(trim($post['post_title'])))649 if ( empty( trim( $post['post_title'] ) ) ) 647 650 $post['post_title'] = 'No title'; 648 651 … … 661 664 ************************************************************************/ 662 665 663 function find_feed( $url) {666 function find_feed( $url ) { 664 667 $links = get_bookmarks(); 665 if ( $links) {666 $url = htmlentities( $url); // link_rss seems to be encoded668 if ( $links ) { 669 $url = htmlentities( $url ); // link_rss seems to be encoded 667 670 foreach ($links as $link) { 668 // $this->logger->debug( 'Testing link «%s» ~ «%s»', $link->link_rss, $url);669 if ( !empty($link->link_rss) and $link->link_rss == $url)671 // $this->logger->debug( 'Testing link «%s» ~ «%s»', $link->link_rss, $url ); 672 if ( !empty( $link->link_rss ) and $link->link_rss == $url ) 670 673 return $link; 671 674 } … … 680 683 ************************************************************************/ 681 684 682 function generate_category_slug( $url) {683 if ( 0 === stripos($url, 'http://'))684 $url = substr( $url, 7);685 elseif ( 0 === stripos($url, 'https://'))686 $url = substr( $url, 8);687 688 return sanitize_title( $url);689 } 690 691 function get_link_category( $link) {692 $cat_slug = $this->generate_category_slug( $link->link_url);693 $cat = get_term_by( 'slug', $cat_slug, 'category');694 695 if ( $cat !== False)685 function generate_category_slug( $url ) { 686 if ( 0 === stripos( $url, 'http://' ) ) 687 $url = substr( $url, 7 ); 688 elseif ( 0 === stripos( $url, 'https://' ) ) 689 $url = substr( $url, 8 ); 690 691 return sanitize_title( $url ); 692 } 693 694 function get_link_category( $link ) { 695 $cat_slug = $this->generate_category_slug( $link->link_url ); 696 $cat = get_term_by( 'slug', $cat_slug, 'category' ); 697 698 if ( $cat !== False ) 696 699 return $cat->term_id; 697 700 698 $this->logger->debug( "Creating category $cat_slug\n");699 700 $cat_id = wp_insert_category( [701 $this->logger->debug( __( "Creating category %d", 'planetplanet' ), $cat_slug ); 702 703 $cat_id = wp_insert_category( [ 701 704 'cat_ID' => 0, 702 705 'cat_name' => $link->link_name, 703 706 'category_nicename' => $cat_slug, 704 'category_description' => sprintf("<!-- %s -->\n", $link->link_url), 705 ], true); 706 707 if (is_wp_error($cat_id)) { 708 $this->logger->error('Failed to create category %s: %s', $cat_slug, $cat_id->get_error_message()); 707 'category_description' => sprintf( "<!-- %s -->\n", $link->link_url ), 708 ], true ); 709 710 if ( is_wp_error( $cat_id ) ) { 711 $this->logger->error( __( 'Failed to create category %s: %s', 'planetplanet' ), 712 $cat_slug, 713 $cat_id->get_error_message() 714 ); 709 715 return false; 710 716 } … … 713 719 } 714 720 715 function get_category_link( $cat) {716 $linkmap = get_transient( 'planetplanet_category_linkmap');717 718 if ( isset($linkmap) and isset($linkmap[$cat->term_id]))721 function get_category_link( $cat ) { 722 $linkmap = get_transient( 'planetplanet_category_linkmap' ); 723 724 if ( isset( $linkmap ) and isset( $linkmap[$cat->term_id] ) ) 719 725 return $linkmap[$cat->term_id]; 720 726 721 727 $linkmap = []; 722 foreach ( get_bookmarks() as $link) {723 $slug = $this->generate_category_slug( $link->link_url);724 $linkcat = get_term_by( 'slug', $slug, 'category');725 726 if ( $linkcat)728 foreach ( get_bookmarks() as $link ) { 729 $slug = $this->generate_category_slug( $link->link_url ); 730 $linkcat = get_term_by( 'slug', $slug, 'category' ); 731 732 if ( $linkcat ) 727 733 $linkmap[$linkcat->term_id] = $link->link_url; 728 734 } 729 set_transient( 'planetplanet_category_linkmap', $linkmap);730 731 return isset( $linkmap[$cat->term_id]) ? $linkmap[$cat->term_id] : NULL;735 set_transient( 'planetplanet_category_linkmap', $linkmap ); 736 737 return isset( $linkmap[$cat->term_id] ) ? $linkmap[$cat->term_id] : NULL; 732 738 } 733 739 … … 738 744 ************************************************************************/ 739 745 740 function import_feed_items( $link) {741 $response = $this->get_web_page( $link->link_rss);742 if ( is_wp_error($response)) return $response;743 744 $data = $this->parse_feed( $response['body']);745 if ( is_wp_error($data)) return $data;746 function import_feed_items( $link ) { 747 $response = $this->get_web_page( $link->link_rss ); 748 if ( is_wp_error( $response ) ) return $response; 749 750 $data = $this->parse_feed( $response['body'] ); 751 if ( is_wp_error( $data ) ) return $data; 746 752 747 753 // find feed category and create if necessary 748 $cat_id = $this->get_link_category( $link);749 $this->logger->debug( "Feed category ID $cat_id\n");754 $cat_id = $this->get_link_category( $link ); 755 $this->logger->debug( __( "Feed category ID %d", 'planetplanet' ), $cat_id ); 750 756 751 757 $posts = $data['posts']; 752 if ( empty($posts)) return false; // nothing to import758 if ( empty( $posts ) ) return false; // nothing to import 753 759 754 760 $limit = NULL; 755 if ($this->has_option( 'retain_limit'))756 $limit = $this->safe_datetime( $this->get_option('retain_limit'));761 if ($this->has_option( 'retain_limit' )) 762 $limit = $this->safe_datetime( $this->get_option('retain_limit') ); 757 763 758 764 $old = 0; … … 761 767 $mtime = NULL; 762 768 763 foreach ( $posts as $post) {764 $dt = new DateTime( $post['post_date']);765 766 if ( !$mtime or $dt > $mtime)769 foreach ( $posts as $post ) { 770 $dt = new DateTime( $post['post_date'] ); 771 772 if ( !$mtime or $dt > $mtime ) 767 773 $mtime = $dt; 768 774 769 if ( $limit) {775 if ( $limit ) { 770 776 // Check post age - this if for recentish stuff 771 777 if ($dt < $limit) { 772 778 $old = $old + 1; 773 $this->logger->debug( "Old post %s - %s", $post['post_title'], $post['post_date']);779 $this->logger->debug( __( "Old post %s - %s", 'planetplanet' ), $post['post_title'], $post['post_date'] ); 774 780 continue; 775 781 } … … 777 783 778 784 // Check that we don't already have the post 779 $olds = get_posts( [785 $olds = get_posts( [ 780 786 'post_type' => 'post', 781 787 'meta_key' => 'item_link', 782 788 'meta_value' => $post['meta_input']['item_link'], 783 ] );784 if ( !empty($olds)) {789 ] ); 790 if ( !empty($olds) ) { 785 791 $seen = $seen + 1; 786 $this->logger->debug( "Seen post %s - %s", $post['post_title'], $post['post_date']);792 $this->logger->debug( __( "Seen post %s - %s", 'planetplanet' ), $post['post_title'], $post['post_date'] ); 787 793 continue; 788 794 } 789 795 790 796 $new = $new + 1; 791 $this->logger->message( "New post found for %s - %s", $link->link_name, $link->link_rss);792 $this->logger->message( " - new post %s - %s", $post['post_title'], $post['meta_input']['item_link']);797 $this->logger->message( __( "New post found for %s - %s", 'planetplanet' ), $link->link_name, $link->link_rss ); 798 $this->logger->message( __( " - new post %s - %s", 'planetplanet' ), $post['post_title'], $post['meta_input']['item_link'] ); 793 799 794 800 // Add category for post 795 801 $post['post_category'] = [ $cat_id ]; 796 $post_id = wp_insert_post( $post);797 if ( is_wp_error($post_id)) return $post_id;798 799 $this->get_featured_image( $post_id);800 } 801 802 if ( $new)803 $this->logger->message( 'Feed status %3d "%s" - %d new, %d seen, %d old',804 $link->link_id, $link->link_name,805 $new, $seen, $old802 $post_id = wp_insert_post( $post ); 803 if ( is_wp_error( $post_id ) ) return $post_id; 804 805 $this->get_featured_image( $post_id ); 806 } 807 808 if ( $new ) 809 $this->logger->message( __( 'Feed status %3d "%s" - %d new, %d seen, %d old', 'planetplanet' ), 810 $link->link_id, $link->link_name, 811 $new, $seen, $old 806 812 ); 807 813 808 return $mtime ? $mtime->format( 'Y-m-d H:i:s') : false;814 return $mtime ? $mtime->format( 'Y-m-d H:i:s' ) : false; 809 815 } 810 816 … … 812 818 // 813 819 814 function get_featured_image( $post_id) {815 if ( !$post_id) return;816 817 $post = get_post( $post_id);818 if ( !$post) return;819 if ( !$post->item_link) return;820 if ( has_post_thumbnail($post)) return;821 822 $response = $this->get_web_page( $post->item_link);823 if ( is_wp_error($response)) return;824 825 $html = mb_convert_encoding( $response['body'], 'HTML-ENTITIES', 'UTF-8');826 $xml = new DOMDocument( '1.0', 'UTF-8');827 $error_mode = libxml_use_internal_errors( true);820 function get_featured_image( $post_id ) { 821 if ( !$post_id ) return; 822 823 $post = get_post( $post_id ); 824 if ( !$post ) return; 825 if ( !$post->item_link ) return; 826 if ( has_post_thumbnail( $post ) ) return; 827 828 $response = $this->get_web_page( $post->item_link ); 829 if ( is_wp_error( $response ) ) return; 830 831 $html = mb_convert_encoding( $response['body'], 'HTML-ENTITIES', 'UTF-8' ); 832 $xml = new DOMDocument( '1.0', 'UTF-8' ); 833 $error_mode = libxml_use_internal_errors( true ); 828 834 829 835 $image_url = NULL; 830 if ( $xml->LoadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD)) {831 $elems = $xml->getElementsByTagName( 'meta');832 833 foreach ( $elems as $elem) {836 if ( $xml->LoadHTML( $html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD ) ) { 837 $elems = $xml->getElementsByTagName( 'meta' ); 838 839 foreach ( $elems as $elem ) { 834 840 $attrs = $elem->attributes; 835 841 836 $attr = $attrs->getNamedItem( 'property');837 if ( isset($attr) and $attr->value == 'og:image') {838 $attr = $attrs->getNamedItem( 'content');839 if ( isset($attr) and !empty($attr->value)) {842 $attr = $attrs->getNamedItem( 'property' ); 843 if ( isset( $attr ) and $attr->value == 'og:image' ) { 844 $attr = $attrs->getNamedItem( 'content' ); 845 if ( isset( $attr ) and !empty( $attr->value ) ) { 840 846 $image_url = $attr->value; 841 847 break; … … 844 850 } 845 851 } 846 libxml_use_internal_errors( $error_mode);847 848 if ( empty($image_url)) {849 $this->logger->message( 'No image found in %s', $post->item_link);852 libxml_use_internal_errors( $error_mode ); 853 854 if ( empty( $image_url ) ) { 855 $this->logger->message( __( 'No image found in %s', 'planetplanet' ), $post->item_link ); 850 856 return; 851 857 }; 852 858 853 $image_url = WP_Http::make_absolute_url( $image_url, $post->item_link);854 855 $this->logger->debug( 'Found image url %s', $image_url);859 $image_url = WP_Http::make_absolute_url( $image_url, $post->item_link ); 860 861 $this->logger->debug( __( 'Found image url %s', 'planetplanet' ), $image_url ); 856 862 857 863 // Check if we already has that image saved 858 $images = get_posts( [864 $images = get_posts( [ 859 865 'post_type' => 'attachment', 860 866 'post_status' => 'inherit', 861 867 'meta_key' => 'source_url', 862 868 'meta_value' => $image_url, 863 ] );864 865 866 if ( empty($images)) {869 ] ); 870 871 872 if ( empty( $images ) ) { 867 873 // Download the image and upload it to the site 868 874 869 $response = $this->get_web_page( $image_url);870 if ( is_wp_error($response)) {871 $this->logger->error( 'Failed to retrieve %s: %s', $image_url, $response->get_error_message(0));875 $response = $this->get_web_page( $image_url ); 876 if ( is_wp_error( $response ) ) { 877 $this->logger->error( __( 'Failed to retrieve %s: %s', 'planetplanet' ), $image_url, $response->get_error_message(0) ); 872 878 return; 873 879 } 874 880 875 881 $bits = $response['body']; 876 $filename = basename( parse_url($image_url, PHP_URL_PATH));882 $filename = basename( parse_url( $image_url, PHP_URL_PATH ) ); 877 883 878 884 // If no filename extension, check http_response_headers 879 if ( empty(pathinfo($filename, PATHINFO_EXTENSION))) {885 if ( empty( pathinfo( $filename, PATHINFO_EXTENSION ) ) ) { 880 886 $content_disp = $response['headers']['Content-Disposition']; 881 887 882 if ( preg_match('/\bfilename="(.*?)"/', $content_disp, $m)) {883 $this->logger->debug( 'Using filename from HTTP response «%s»', $m[1]);888 if ( preg_match( '/\bfilename="(.*?)"/', $content_disp, $m ) ) { 889 $this->logger->debug( __( 'Using filename from HTTP response «%s»', 'planetplanet' ), $m[1] ); 884 890 $filename = $m[1]; 885 891 } 886 elseif ( preg_match('/\bfilename=(.*?)$/', $content_disp, $m)) {887 $this->logger->debug( 'Using filename from HTTP response «%s»', $m[1]);892 elseif ( preg_match( '/\bfilename=(.*?)$/', $content_disp, $m ) ) { 893 $this->logger->debug( __( 'Using filename from HTTP response «%s»', 'planetplanet' ), $m[1] ); 888 894 $filename = $m[1]; 889 895 } 890 896 } 891 897 892 $upload_file = wp_upload_bits( $filename, NULL, $bits);893 894 if ( $upload_file['error']) {895 $this->loggger->error( 'Image upload error %s', $upload_file['error']);898 $upload_file = wp_upload_bits( $filename, NULL, $bits ); 899 900 if ( $upload_file['error'] ) { 901 $this->loggger->error( __( 'Image upload error %s', 'planetplanet' ), $upload_file['error'] ); 896 902 return; 897 903 } 898 904 899 $filetype = wp_check_filetype( $upload_file['file'], NULL);905 $filetype = wp_check_filetype( $upload_file['file'], NULL ); 900 906 901 907 $attachment = [ 902 908 'guid' => $upload_file['url'], 903 909 'post_mime_type' => $filetype['type'], 904 'post_title' => sanitize_file_name( $filename),910 'post_title' => sanitize_file_name( $filename ), 905 911 'post_content' => '', 906 912 'post_status' => 'inherit', … … 908 914 ]; 909 915 910 $this->logger->debug( 'Registering attachment %s', $upload_file['file']);911 912 $attachment_id = wp_insert_attachment( $attachment, $upload_file['file'], $post->ID, true);913 if ( is_wp_error($attachment_id)) {914 $this->logger->error( 'Image attachment error %s', $attachment_id->get_error_message());916 $this->logger->debug( __( 'Registering attachment %s', 'planetplanet' ), $upload_file['file'] ); 917 918 $attachment_id = wp_insert_attachment( $attachment, $upload_file['file'], $post->ID, true ); 919 if ( is_wp_error( $attachment_id ) ) { 920 $this->logger->error( __( 'Image attachment error %s', 'planetplanet' ), $attachment_id->get_error_message() ); 915 921 return; 916 922 } 917 923 918 $this->logger->debug( 'Created attachment id %d', $attachment_id);919 920 $attachment_data = wp_generate_attachment_metadata( $attachment_id, $upload_file['file']);921 wp_update_attachment_metadata( $attachment_id, $attachment_data);924 $this->logger->debug( __( 'Created attachment id %d', 'planetplanet' ), $attachment_id ); 925 926 $attachment_data = wp_generate_attachment_metadata( $attachment_id, $upload_file['file'] ); 927 wp_update_attachment_metadata( $attachment_id, $attachment_data ); 922 928 923 929 // Save the source of the image 924 add_post_meta( $attachment_id, 'source_url', $image_url);930 add_post_meta( $attachment_id, 'source_url', $image_url ); 925 931 } else { 926 932 $attachment_id = $images[0]->ID; 927 $this->logger->debug( 'Using existing attachment id %d', $attachment_id);933 $this->logger->debug( __( 'Using existing attachment id %d', 'planetplanet' ), $attachment_id ); 928 934 } 929 935 930 936 // Attach image to post 931 $success = set_post_thumbnail( $post, $attachment_id);932 if ( !$success) return;933 934 $this->logger->message( 'Downloaded post thumbnail');937 $success = set_post_thumbnail( $post, $attachment_id ); 938 if ( !$success ) return; 939 940 $this->logger->message( __( 'Downloaded post thumbnail', 'planetplanet' ) ); 935 941 return $success; 936 942 } … … 939 945 // WP db links 940 946 941 function update_feed( $link) {942 $this->logger->debug( 'Starting import of %s - %s', $link->link_name, $link->link_url);943 944 $mtime = $this->import_feed_items( $link);945 946 if ( is_wp_error($mtime)) {947 function update_feed( $link ) { 948 $this->logger->debug( __( 'Starting import of %s - %s', 'planetplanet' ), $link->link_name, $link->link_url ); 949 950 $mtime = $this->import_feed_items( $link ); 951 952 if ( is_wp_error( $mtime ) ) { 947 953 $msg = $mtime->get_error_message(); 948 $link->link_notes = substr( $msg, 0, 255);954 $link->link_notes = substr( $msg, 0, 255 ); 949 955 $link->link_rating = $link->link_rating + 1; 950 956 951 $max_errors = $this->get_option( 'max_errors') ?? 5;952 953 $this->logger->error( 'Feed %d "%s" - Error #%d/%d: %s: %s',954 $link->link_id,955 $link->link_name,956 $link->link_rating,957 $max_errors,958 $link->link_rss,959 $msg957 $max_errors = $this->get_option( 'max_errors' ) ?? 5; 958 959 $this->logger->error( __( 'Feed %d "%s" - Error #%d/%d: %s: %s', 'planetplanet' ), 960 $link->link_id, 961 $link->link_name, 962 $link->link_rating, 963 $max_errors, 964 $link->link_rss, 965 $msg 960 966 ); 961 967 962 if ($link->link_rating >= $max_errors) { 963 $this->logger->error('Feed %d "%s" suspended: too many errors', $link->link_id, $link->link_name); 968 if ( $link->link_rating >= $max_errors ) { 969 $this->logger->error( __( 'Feed %d "%s" suspended: too many errors', 'planetplanet' ), 970 $link->link_id, $link->link_name ); 964 971 $link->link_visible = 'N'; 965 972 } … … 971 978 } 972 979 973 $this->logger->debug('Updating %d "%s" - mtime %s', $link->link_id, $link->link_name, $mtime); 974 $link_id = wp_insert_link(get_object_vars($link), true); 975 if (is_wp_error($link_id)) 976 $this->logger->error('Saving link %d failed: %s', $link->link_id, $link_id->get_error_message()); 980 $this->logger->debug( __( 'Updating %d "%s" - mtime %s', 'planetplanet' ), 981 $link->link_id, $link->link_name, $mtime ); 982 $link_id = wp_insert_link( get_object_vars( $link ), true ); 983 if ( is_wp_error( $link_id ) ) 984 $this->logger->error( __( 'Saving link %d failed: %s', 'planetplanet' ), 985 $link->link_id, $link_id->get_error_message() ); 977 986 978 987 // NOT NICE - wp_update_link() doesn't update the link_updated field 979 988 980 if ( $mtime) {989 if ( $mtime ) { 981 990 global $wpdb; 982 $wpdb->update( "{$wpdb->prefix}links",983 [ 'link_updated' => $mtime ],984 [ 'link_id' => $link->link_id ]991 $wpdb->update( "{$wpdb->prefix}links", 992 [ 'link_updated' => $mtime ], 993 [ 'link_id' => $link->link_id ] 985 994 ); 986 995 } … … 989 998 function update_all_feeds() { 990 999 $links = get_bookmarks(); 991 if ( empty($links)) return;1000 if ( empty( $links ) ) return; 992 1001 993 1002 $now = time(); 994 $this->logger->debug( 'START %s', current_time('mysql'));995 foreach ( $links as $link) {996 $this->update_feed( $link);997 } 998 $this->logger->debug( 'END %s - elapsed %ds', current_time('mysql'), time()-$now);1003 $this->logger->debug( __( 'START %s', 'planetplanet' ), current_time('mysql') ); 1004 foreach ( $links as $link ) { 1005 $this->update_feed( $link ); 1006 } 1007 $this->logger->debug( __( 'END %s - elapsed %ds', 'planetplanet' ), current_time( 'mysql' ), time() - $now ); 999 1008 } 1000 1009 … … 1007 1016 1008 1017 function purge_posts() { 1009 $limit = $this->get_option( 'retain_limit');1010 if ( !$limit) return;1011 if ( !$this->safe_datetime($limit)) return;1018 $limit = $this->get_option( 'retain_limit' ); 1019 if ( !$limit ) return; 1020 if ( !$this->safe_datetime( $limit ) ) return; 1012 1021 1013 1022 // Query to find some of the oldest posts before the retain limit … … 1020 1029 ]; 1021 1030 1022 while (!empty($posts = get_posts($query))) { 1023 foreach ($posts as $post) { 1024 $this->logger->debug('Found post %4d %s %s', $post->ID, $post->post_date, $post->post_title); 1031 while ( !empty( $posts = get_posts( $query ) ) ) { 1032 foreach ( $posts as $post ) { 1033 $this->logger->debug( __( 'Found post %4d %s %s', 'planetplanet' ), 1034 $post->ID, $post->post_date, $post->post_title ); 1025 1035 1026 1036 $image_id = get_post_thumbnail_id($post->ID); 1027 if ($image_id) { 1028 $image = get_post($image_id); 1029 $this->logger->debug('> found image %4d %s %s', $image_id, $image->post_date, $image->post_title); 1030 1031 if ($image) { 1032 $image_posts = get_posts([ 1037 if ( $image_id ) { 1038 $image = get_post( $image_id ); 1039 $this->logger->debug( __( '> found image %4d %s %s', 'planetplanet' ), 1040 $image_id, $image->post_date, $image->post_title ); 1041 1042 if ( $image ) { 1043 $image_posts = get_posts( [ 1033 1044 'meta_key' => '_thumbnail_id', 1034 1045 'meta_value' => $image_id, 1035 1046 'numberposts' => -1, 1036 ]); 1037 1038 $this->logger->debug('>> image is attached to %d posts', count($image_posts)); 1047 ] ); 1048 1049 $this->logger->debug( __( '>> image is attached to %d posts', 'planetplanet' ), 1050 count($image_posts) ); 1039 1051 1040 1052 // Delete image only if not in use by other posts 1041 if (1 == count($image_posts)) { 1042 if (wp_delete_attachment($image_id, true)) 1043 $this->logger->message('Deleted image %d - %s', $image_id, $image->post_title); 1053 if ( 1 == count( $image_posts ) ) { 1054 if ( wp_delete_attachment( $image_id, true ) ) 1055 $this->logger->message( __( 'Deleted image %d - %s', 'planetplanet' ), 1056 $image_id, $image->post_title ); 1044 1057 else 1045 $this->logger->error('Deletion of image %d - %s failed', $image_id, $image->post_title); 1058 $this->logger->error( __( 'Deletion of image %d - %s failed', 'planetplanet' ), 1059 $image_id, $image->post_title ); 1046 1060 } 1047 1061 } 1048 1062 } 1049 1063 1050 if (wp_delete_post($post->ID, true)) 1051 $this->logger->message('Deleted post %d - %s', $post->ID, $post->post_title); 1064 if ( wp_delete_post( $post->ID, true ) ) 1065 $this->logger->message( __( 'Deleted post %d - %s', 'planetplanet' ), 1066 $post->ID, $post->post_title ); 1052 1067 else 1053 $this->logger->error('Deletion of post %d - %s failed', $post->ID, $post->post_title); 1068 $this->logger->error( __( 'Deletion of post %d - %s failed', 'planetplanet' ), 1069 $post->ID, $post->post_title ); 1054 1070 } 1055 1071 } -
planetplanet/trunk/readme.txt
r2651218 r2651566 1 1 === PlanetPlanet === 2 2 Contributors: seindal 3 Donate Link: https://www.seindal.dk/4 3 Tags: rss-aggregator 5 4 Requires at least: 5.8 … … 10 9 License URI: https://www.gnu.org/licenses/gpl-2.0.html 11 10 12 Convert a wordpress site into an RSS/Atom feed aggregator like the old 'planet' software.11 Convert a WordPress site into an RSS/Atom feed aggregator like the old 'planet' software. 13 12 14 13 == Description == 15 14 16 The PlanetPlanet plugin will convert a wordpress site into an RSS/Atom feed aggregator by regularly reading all the configured feeds and importing the posts in the feeds to Wordpress posts.15 The PlanetPlanet plugin will convert a WordPress site into an RSS/Atom feed aggregator by regularly reading all the configured feeds and importing the items in the feeds to WordPress posts. 17 16 18 The feeds are registered in the old Word press links/bookmarks section, which is automatically reactivated. Feeds can be added, modified and deleted there.17 The feeds are registered in the old WordPress links/bookmarks section, which is automatically reactivated. Feeds can be added, modified and deleted there. 19 18 20 19 The imported posts contain title, excerpt and maybe content from the feed items. Additional custom fields are added with some data that doesn't intuitively map to WP posts. If the feed item has a featured image, that is imported too. … … 28 27 Importing and purging articles can be scheduled using wp-cron. Output from scheduled actions can be mailed. 29 28 30 The plugin adds a WP-CLI command 'planet' with sub commands for listing feeds, adding feeds, updating individual or all feeds, purging posts, and scanning web pages for available feeds. This allows most adminstration and actions to be run without using wp-cron.29 The plugin adds a WP-CLI command 'planet' with sub-commands for listing feeds, adding feeds, updating individual or all feeds, purging posts, and scanning web pages for available feeds. This allows most administration and actions to be run without using wp-cron. 31 30 32 Im lementation notes: feeds errors are counted in the link_rating field, and the feed is marked not visible if it generates too many errors.31 Implementation notes: feeds errors are counted in the link_rating field, and the feed is marked not visible if it generates too many errors. 33 32 34 33 == Installation == … … 40 39 == Configuration == 41 40 42 The plugin adds a 'Planet Planet' sub menu under Settings.41 The plugin adds a 'Planet Planet' sub-menu under Settings. 43 42 44 43 * How often to check feeds: the choices are from the WP scheduler. 'None' means no automatic updates. The site can still be updated through the WP-CLI interface. … … 46 45 * Discard posts older than this: the value can be anything the PHP class DateTime can parse into a past date, which includes values like '6 months ago'. Too old posts are never imported, and they're purged automatically. If the field is empty, even very old feed items are imported and never purged. 47 46 48 * Number of errors before feed is suspended: see below 47 * Number of errors before feed is suspended: see below. 49 48 50 49 * Email for updates: insert an email if you want the output from schedule actions (updates and purges) mailed to you. Leave empty for no mails. … … 56 55 * User-Agent: some servers filter on the User-Agent header. 57 56 57 == How to setup an RSS feed aggregator site == 58 59 1. Setup and configure an empty WordPress site 60 2. Install and activate the PlanetPlanet plugin 61 3. Setup automatic updates either through the plugin configuration page or externally through WP-CLI. 62 4. Add links in the "Links" section on the right hand admin menu. You need to add Link name, URL and RSS URL for each feed. 63 5. Find or create a WP theme that shows the posts the way you want, remembering that certain links will redirect automatically to the originating site of the posts. 64 58 65 == Feed errors == 59 66 … … 62 69 If a feed generates too many (configurable) consecutive errors, it is marked as 'not visible'. It will not be updated any more. It can always be reactivated in the links manager. 63 70 64 Old posts from a disabled feed are not removed. They can be easily identifed through the post category for the feed.71 Old posts from a disabled or deleted feed are not removed. They can be easily identified through the post category for the feed. 65 72 66 The error count and the saved error message are reset with each succes ful update.73 The error count and the saved error message are reset with each successful update. 67 74 68 75 == Frequently Asked Questions == … … 81 88 == Screenshots == 82 89 83 The only visible part of this plugin is the sett tings submenu.90 The only visible part of this plugin is the settings sub-menu.
Note: See TracChangeset
for help on using the changeset viewer.