· 5 years ago · Oct 14, 2020, 12:08 PM
1<?php
2/**
3 * Plugin Name: Syndication Network
4 * Description: A tiny plugin that fetches articles from Bing API and press releases from Skynet site.
5 * Version: 1.0
6 * Requires at least: 3.8
7 */
8
9class Crb_Syndication_Network {
10
11 protected $api_key;
12
13 protected $api_url;
14
15 protected $search_term;
16
17 protected $count;
18
19 protected $press_releases_url;
20
21 protected $mkt = 'en-us';
22
23 /**
24 * Constructor.
25 *
26 * Initializes hooks
27 */
28 public function __construct() {
29 add_action( 'admin_menu', array( $this, 'init' ) );
30 add_action( 'admin_init', array( $this, 'save_options' ) );
31
32 add_action( 'wp', array( $this, 'activate_cron_job' ) );
33 add_action( 'fetch_articles_action', array( $this, 'fetch_articles' ) );
34
35 add_action( 'add_meta_boxes', array( $this, 'add_press_release_metaboxes' ) );
36 add_action( 'save_post', array( $this, 'save_press_release_meta_fields' ) );
37
38 add_action( 'fetch_press_releases_action', array( $this, 'fetch_press_releases' ) );
39 add_action( 'fetch_press_releases_content_action', array( $this, 'fetch_press_releases_content' ) );
40 add_action( 'delete_old_articles_action', array( $this, 'delete_old_articles' ) );
41
42 add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_assets' ) );
43 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_admin_assets' ) );
44
45 add_action( 'init', array( $this, 'register_press_releases_post_type' ) );
46
47 /* Filter the single_template with custom function*/
48 add_filter( 'single_template', array( $this, 'press_releases_template' ) );
49
50 add_shortcode( 'sn_print_articles', array( $this, 'print_articles' ) );
51 add_shortcode( 'sn_press_releases', array( $this, 'print_press_releases' ) );
52
53 add_filter('excerpt_more', array( $this, 'remove_excerpt_read_more_link' ), 20 );
54
55 add_action( 'template_redirect', array( $this, 'modify_dates' ) );
56
57 }
58
59 /**
60 * Initialize the plugin functionality.
61 * Will not bother users who lack the manage_options capability.
62 */
63 public function init() {
64 // bail for non-logged in users
65 if (!is_user_logged_in()) {
66 return;
67 }
68 add_options_page( 'MEK Syndication Network', 'MEK Syndication Network' , 'manage_options', 'syndication-network', [$this, 'settings_page'], 11 );
69 }
70
71 public function enqueue_assets() {
72 wp_enqueue_style( 'sn-style', plugin_dir_url( __FILE__ ) . 'css/style.css', array(), null, 'all' );
73 }
74
75 public function enqueue_admin_assets() {
76 wp_enqueue_style( 'sn-admin-style', plugin_dir_url( __FILE__ ) . 'css/admin-style.css', array(), null, 'all' );
77 }
78
79 public function register_press_releases_post_type() {
80 register_post_type( 'sn_press_release', array(
81 'labels' => array(
82 'name' => __( 'Press Releases', 'crb' ),
83 'singular_name' => __( 'Press Release', 'crb' ),
84 'add_new' => __( 'Add New', 'crb' ),
85 'add_new_item' => __( 'Add new Press Release', 'crb' ),
86 'view_item' => __( 'View Press Release', 'crb' ),
87 'edit_item' => __( 'Edit Press Release', 'crb' ),
88 'new_item' => __( 'New Press Release', 'crb' ),
89 'view_item' => __( 'View Press Release', 'crb' ),
90 'search_items' => __( 'Search Press Releases', 'crb' ),
91 'not_found' =>__( 'No Press Releases found', 'crb' ),
92 'not_found_in_trash' => __( 'No Press Releases found in trash', 'crb' ),
93 ),
94 'public' => true,
95 'exclude_from_search' => false,
96 'show_ui' => true,
97 'capability_type' => 'post',
98 'hierarchical' => false,
99 '_edit_link' => 'post.php?post=%d',
100 'rewrite' => array(
101 'slug' => 'press-release',
102 'with_front' => false,
103 ),
104 'query_var' => true,
105 'menu_icon' => 'dashicons-media-text',
106 'supports' => array( 'title', 'editor', 'page-attributes' ),
107 ) );
108
109 global $wp_rewrite;
110 $wp_rewrite->flush_rules(false);
111
112 }
113
114 public function activate_cron_job() {
115 if ( !wp_next_scheduled( 'fetch_articles_action' ) ) {
116 wp_schedule_event( time(), 'daily', 'fetch_articles_action' );
117 }
118
119 if ( !wp_next_scheduled( 'fetch_press_releases_action' ) ) {
120 wp_schedule_event( time(), 'daily', 'fetch_press_releases_action' );
121 }
122
123 if ( !wp_next_scheduled( 'fetch_press_releases_content_action' ) ) {
124 wp_schedule_event( time(), 'hourly', 'fetch_press_releases_content_action' );
125 }
126
127 if ( !wp_next_scheduled( 'delete_old_articles_action' ) ) {
128 wp_schedule_event( time(), 'daily', 'delete_old_articles_action' );
129 }
130 }
131
132 public function add_press_release_metaboxes() {
133 add_meta_box( 'sn_press_release_options_meta_box', 'Press Release Options', array( $this, 'press_release_meta_fields' ), 'sn_press_release', 'normal', 'high' );
134 }
135
136 public function press_release_meta_fields() {
137 global $post;
138
139 $article_url = get_post_meta( get_the_ID(), 'press_release_article_url', 1 );
140 $date = get_post_meta( get_the_ID(), 'press_release_date', 1 );
141 $order_id = get_post_meta( get_the_ID(), 'press_release_order_id', 1 );
142 // Use nonce for verification to secure data sending
143 wp_nonce_field( basename( __FILE__ ), 'sn_press_release_nonce' ); ?>
144
145 <ul class="press-release-fields">
146 <li>
147 <label for="press-release-article-url">Article URL: </label>
148 <input id="press-release-article-url" type="text" name="press_release_article_url" value="<?php echo $article_url; ?>" />
149 </li>
150
151 <li>
152 <label for="press-release-date">Date: </label>
153 <input id="press-release-date" type="text" name="press_release_date" value="<?php echo $date; ?>" />
154 </li>
155 <li>
156 <label for="press-release-order-id">Order ID: </label>
157 <input id="press-release-order-id" type="text" name="press_release_order_id" value="<?php echo $order_id; ?>" />
158 </li>
159 </ul>
160 <?php
161 }
162
163 public function press_releases_template( $single ) {
164 global $post;
165 /* Checks for single template by post type */
166 if ( $post->post_type == 'sn_press_release' ) {
167 if ( file_exists( plugin_dir_path( __FILE__ ) . '/single-press-release.php' ) ) {
168 return plugin_dir_path( __FILE__ ) . '/single-press-release.php';
169 }
170 }
171 return $single;
172 }
173
174 //now we are saving the data
175 public function save_press_release_meta_fields( $post_id ) {
176
177 // verify nonce
178 if ( !isset( $_POST['sn_press_release_nonce'] ) || !wp_verify_nonce( $_POST['sn_press_release_nonce'], basename( __FILE__ ) ) ) {
179 return 'nonce not verified';
180 }
181
182 // check autosave
183 if ( wp_is_post_autosave( $post_id ) ) {
184 return 'autosave';
185 }
186
187 //check post revision
188 if ( wp_is_post_revision( $post_id ) ) {
189 return 'revision';
190 }
191
192 // check permissions
193 if ( 'sn_press_release' == $_POST['post_type'] ) {
194 if ( ! current_user_can( 'edit_page', $post_id ) ) {
195 return 'cannot edit page';
196 } elseif ( ! current_user_can( 'edit_post', $post_id ) ) {
197 return 'cannot edit post';
198 }
199 }
200
201 //so our basic checking is done, now we can grab what we've passed from our newly created form
202 $article_url = $_POST['press_release_article_url'];
203 $date = $_POST['press_release_date'];
204 $order_id = $_POST['press_release_order_id'];
205
206 update_post_meta( $post_id, 'press_release_article_url', $article_url );
207 update_post_meta( $post_id, 'press_release_date', $date );
208 update_post_meta( $post_id, 'press_release_order_id', $order_id );
209
210
211 }
212
213 public function fetch_articles() {
214
215 require_once( ABSPATH . 'wp-admin/includes/post.php' );
216
217 $this->init_options();
218
219 $handle = curl_init();
220
221 $this->search_term = urlencode( $this->search_term );
222 $url = $this->api_url . '?q=' . $this->search_term . '&count=' . $this->count . '&offset=0&mkt=' . $this->mkt . '&safeSearch=Moderate';
223
224 $headers = [
225 'Host: eastus.cognitiveservices.azure.com',
226 'Ocp-Apim-Subscription-Key: ' . $this->api_key
227 ];
228
229 curl_setopt( $handle, CURLOPT_HTTPHEADER, $headers );
230 curl_setopt_array( $handle, array(
231 CURLOPT_URL => $url,
232 // Enable the post response.
233 CURLOPT_CUSTOMREQUEST => 'GET',
234 CURLOPT_POST => false,
235 // The data to transfer with the response.
236 CURLOPT_RETURNTRANSFER => true,
237 CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1
238 ));
239
240 $data = curl_exec( $handle );
241
242 if ( $data ) {
243 $data = json_decode( $data );
244 $articles = $data->value;
245
246 foreach ( $articles as $article ) {
247 if ( post_exists( $article->name ) ) {
248 continue;
249 }
250
251 $post_id = wp_insert_post([
252 'post_type' => 'post',
253 'post_date' => $article->datePublished,
254 'post_title' => $article->name,
255 'post_content' => $article->description,
256 'post_status' => 'publish'
257 ]);
258
259 if ( is_wp_error( $post_id ) ) {
260 continue;
261 }
262
263 update_post_meta( $post_id, '_sn_post_url', $article->url );
264
265 // Handle Image
266 if ( isset( $article->image ) ) {
267 $image = $article->image->thumbnail;
268 $image_url = $image->contentUrl;
269 if ( isset( $image->width ) ) {
270 $image_url .= '&w=' . $image->width;
271 }
272
273 $this->set_remote_image_as_post_thumbnail( $post_id, $image_url );
274 }
275 }
276 }
277 }
278
279 public function set_remote_image_as_post_thumbnail( $post_id, $image_url ) {
280 include_once( ABSPATH . '/wp-admin/includes/image.php' );
281
282 $imagetype = end( explode( '/', getimagesize( $image_url )['mime'] ) );
283 $unique_name = date( 'dmY' ) . '' . wp_generate_password(8, false, false);
284 $filename = $unique_name . '.' . $imagetype;
285
286 $uploaddir = wp_upload_dir();
287 $uploadfile = $uploaddir['path'] . '/' . $filename;
288 $contents= file_get_contents( $image_url );
289 $savefile = fopen( $uploadfile, 'w' );
290 fwrite( $savefile, $contents );
291 fclose( $savefile);
292
293 $wp_filetype = wp_check_filetype( basename( $filename ), null );
294 $attachment = array(
295 'post_mime_type' => $wp_filetype['type'],
296 'post_title' => $filename,
297 'post_content' => '',
298 'post_status' => 'inherit'
299 );
300
301 $attach_id = wp_insert_attachment( $attachment, $uploadfile );
302 $new_image = get_post( $attach_id );
303 $full_size_path = get_attached_file( $new_image->ID );
304 $attach_data = wp_generate_attachment_metadata( $attach_id, $full_size_path );
305 wp_update_attachment_metadata( $attach_id, $attach_data );
306
307 set_post_thumbnail( $post_id, $attach_id );
308 }
309
310
311 public function fetch_press_releases() {
312 $press_releases_url = get_option( 'sn_press_releases_url' );
313 if ( !$press_releases_url ) {
314 return;
315 }
316
317 $data = wp_remote_get( $press_releases_url );
318 if ( !$data['body'] ) {
319 return;
320 }
321
322 $press_releases_data = json_decode( $data['body'] );
323 if ( !$press_releases_data->data ) {
324 return;
325 }
326
327 $press_releases = $press_releases_data->data;
328 foreach ( $press_releases as $count => $press_release ) {
329
330 // If the press releases already exists, do no add it again.
331 if ( get_post( $press_release->id ) ) {
332 continue;
333 }
334
335 // If the articles url is empty or null, do not fetch it yet
336 if ( !$press_release->article_url || is_null( $press_release->article_url ) || $press_release->article_url == '' ) {
337 continue;
338 }
339
340 $id = wp_insert_post( [
341 'import_id' => $press_release->id,
342 'post_type' => 'sn_press_release',
343 'post_status' => 'publish',
344 'post_title' => $press_release->name,
345 'post_date' => date( 'Y-m-d H:i:s', strtotime( $press_release->date ) ),
346 ]);
347
348 update_post_meta( $id, 'press_release_date', $press_release->date );
349 update_post_meta( $id, 'press_release_article_url', $press_release->article_url );
350 update_post_meta( $id, 'press_release_order_id', $press_release->order_id );
351 update_post_meta( $id, 'press_release_content_updated', 'no' );
352 }
353
354 }
355
356 public function fetch_press_releases_content() {
357
358
359 $press_releases = new WP_Query([
360 'post_type' => 'sn_press_release',
361 'posts_per_page' => -1,
362 'meta_query' => [
363 [
364 'key' => 'press_release_content_updated',
365 'value' => 'no',
366 'compare' => '='
367 ]
368 ]
369 ]);
370
371 if ( !$press_releases->have_posts() ) {
372 return;
373 }
374
375 include_once( 'phpquery.php' );
376
377 while ( $press_releases->have_posts() ) {
378 $press_releases->the_post();
379 $article_url = get_post_meta( get_the_ID(), 'press_release_article_url', 1 );
380 $article_order_id = get_post_meta( get_the_ID(), 'press_release_order_id', 1 );
381
382 if ( !$article_url || $article_url == 'null' ) {
383 continue;
384 }
385
386 $article_content = wp_remote_get( $article_url );
387 if ( is_wp_error( $article_content ) ) {
388 continue;
389 }
390 $doc = phpQuery::newDocumentHTML( $article_content['body'] );
391 $release_body = pq( '.release-body' );
392
393 $article_content = $release_body->html();
394
395 $article_email = $article_order_id . '@email4pr.com';
396 $article_email_html = '<br/><a href="mailto:' . $article_email . '">' . $article_email . '</a><br/>';
397
398 $article_content = preg_replace("~\<a href=[\"|\']\/cdn.*\<\/a\>~", $article_email_html, $article_content );
399
400 wp_update_post([
401 'ID' => get_the_ID(),
402 'post_content' => $article_content
403 ]);
404
405 update_post_meta( get_the_ID(), 'press_release_content_updated', 'yes' );
406 }
407
408 wp_reset_postdata();
409 }
410
411 public function get_options_array() {
412 return [ 'api_key', 'api_url', 'search_term', 'count', 'press_releases_url' ];
413 }
414
415 public function init_options() {
416 $options = $this->get_options_array();
417
418 foreach ( $options as $option ) {
419 $this->{$option} = get_option( 'sn_' . $option );
420 }
421 }
422
423 public function save_options() {
424 if ( !isset( $_POST['sn_api_key'] ) ) {
425 return;
426 }
427
428 $options = $this->get_options_array();
429
430 foreach ( $options as $option ) {
431 $option_key = 'sn_' . $option;
432 update_option( $option_key, $_POST[$option_key] );
433 }
434 }
435
436 function settings_page() {
437 $this->init_options();
438
439 $protocols = array( 'http://', 'http://www.', 'www.', 'https://', 'https://www.');
440 $site_domain = str_replace( $protocols, '', get_bloginfo( 'wpurl' ) ); ?>
441 <div class="syndication-network-settings">
442 <h3>Settings</h3>
443 <form action="" method="POST" />
444 <ul>
445 <li>
446 <label for="api-key">API Key:</label>
447 <input id="api-key" name="sn_api_key" value="<?php echo $this->api_key; ?>" />
448 <p>This key is obtained from your Microsoft Azure cpanel. And it is required to make requests to the API URL.</p>
449 </li>
450
451 <li>
452 <label for="api-url">API URL:</label>
453 <input id="api-url" name="sn_api_url" value="<?php echo $this->api_url; ?>" />
454 <p>This URL is obtained from your Microsoft Azure cpanel.</p>
455 </li>
456
457 <li>
458 <label for="search-term">Search Term:</label>
459 <input id="search-term" name="sn_search_term" value="<?php echo $this->search_term; ?>" />
460 <p>The Bing API returns articles which are related to this keyword.</p>
461 </li>
462
463 <li>
464 <label for="count">Articles Number:</label>
465 <input id="count" name="sn_count" value="<?php echo $this->count; ?>" />
466 </li>
467
468 <li><hr /></li>
469
470 <li>
471 <label for="press-releases-url">Press Releases URL:</label>
472 <input id="press-releases-url" name="sn_press_releases_url" value="<?php echo $this->press_releases_url; ?>" /><br/><br/>
473 <em>Please enter the following in the field above: <code>https://order.ereleases.com/api/press-releases?domain=<?php echo $site_domain; ?></code>.<br/>
474 Please note: the text after <code>?domain=</code> should match the domain in Skynet -> Domains section.</em>
475 </li>
476 </ul>
477
478 <input type="submit" value="Save" class="button" />
479 </form>
480 <p>The system automatically fetches the articles from the Bing API and press releases from Skynet ( http://order.ereleases.com/ ) website. The content gets refreshed every day.</p>
481
482 <h2>Shortcodes:</h2>
483 <ul>
484 <li>[sn_print_articles] - Prints the latest articles from Bing API, based on the search term.</li>
485 <li>[sn_press_releases] - Displays the latest press releases from Skynet ( 10 press releases per page ).</li>
486 </ul>
487 </div>
488 <?php
489 }
490
491 public function print_articles( $args, $content ) {
492 $articles = new WP_Query([
493 'posts_per_page' => $this->count
494 ]);
495 if ( !$articles->have_posts() ) {
496 return;
497 }
498 ob_start(); ?>
499
500 <section class="section-articles">
501 <div class="shell">
502 <div class="section__body">
503 <div class="articles">
504 <ul class="articles__items">
505 <?php while ( $articles->have_posts() ) :
506
507 $articles->the_post();
508 $url = get_post_meta( get_the_ID(), '_sn_post_url', true );?>
509 <li>
510 <div class="article-item">
511 <a href="<?php echo $url; ?>" target="_blank"></a>
512
513 <?php if ( has_post_thumbnail() ) : ?>
514 <div class="article-item__image">
515 <span class="image-fit">
516 <?php the_post_thumbnail(); ?>
517 </span>
518 </div><!-- /.article-item__image -->
519
520 <?php endif; ?>
521
522 <div class="article-item__meta">
523 <p><?php echo get_the_date( 'd M Y' ); ?></p>
524 </div><!-- /.article-item__meta -->
525
526 <div class="article-item__content">
527 <h3><?php the_title(); ?></h3>
528
529 <?php the_excerpt(); ?>
530 </div><!-- /.article-item__content -->
531 </div><!-- /.article-item -->
532 </li>
533
534 <?php endwhile;
535 wp_reset_postdata();
536 ?>
537 </ul>
538 </div><!-- /.articles -->
539 </div><!-- /.section__body -->
540
541 </div><!-- /.shell -->
542 </section><!-- /.section-articles -->
543 <?php return ob_get_clean();
544 }
545
546 public function print_press_releases() {
547 $press_releases = new WP_Query( [
548 'post_type' => 'sn_press_release',
549 'posts_per_page' => 10,
550 'paged' => get_query_var( 'paged' ) ? get_query_var( 'paged' ) : 1,
551 ] );
552
553 ob_start();
554 ?>
555 <div class="press-releases">
556 <?php if ( $press_releases->have_posts() ) : ?>
557 <ul>
558 <?php while ( $press_releases->have_posts() ) :
559
560 $press_releases->the_post(); ?>
561 <li>
562 <h4>
563 <a href="<?php the_permalink() ?>"><?php the_title(); ?></a>
564 </h4>
565 <?php the_excerpt(); ?>
566 </li>
567
568 <?php endwhile; ?>
569 </ul>
570 <?php
571 wp_reset_postdata();
572
573 $next_posts_link = get_next_posts_link( esc_html__( 'Previous Press Releases »', 'crb' ), $press_releases->max_num_pages );
574 $previous_posts_link = get_previous_posts_link( esc_html__( '« Next Press Releases', 'crb' ) );
575
576 if ( $next_posts_link || $previous_posts_link ) : ?>
577 <div class="section__actions">
578 <?php printf( '%s %s', $previous_posts_link, $next_posts_link ); ?>
579 </div><!-- /.paging -->
580 <?php endif; ?>
581 <?php else : ?>
582 <p><?php _e( 'There are no press releases.' ); ?></p>
583 <?php endif; ?>
584 </div>
585
586 <?php return ob_get_clean();
587 }
588
589 public function remove_excerpt_read_more_link( $more ) {
590 return '';
591 }
592
593 public function delete_old_articles() {
594 $articles = new WP_Query([
595 'posts_per_page' => -1,
596 'date_query' => [
597 'before' => date( 'Y-m-d', strtotime( '-14 days' ) )
598 ]
599 ]);
600
601 if ( !$articles->have_posts() ) {
602 return;
603 }
604
605 while ( $articles->have_posts() ) {
606 $articles->the_post();
607 wp_delete_post( get_the_ID(), true );
608 }
609
610 wp_reset_postdata();
611 }
612
613 public function modify_dates() {
614 if ( !isset( $_GET['modify_press_releases_dates'] ) ) {
615 return;
616 }
617
618 $press_releases = new WP_Query([
619 'post_type' => 'sn_press_release',
620 'posts_per_page' => -1
621 ]);
622
623 while ( $press_releases->have_posts() ) {
624 $press_releases->the_post();
625 $date = get_post_meta( get_the_ID(), 'press_release_date', 1 );
626 wp_update_post([
627 'ID' => get_the_ID(),
628 'post_date' => date( 'Y-m-d H:i:s', strtotime( $date ) )
629 ]);
630 }
631
632 wp_reset_postdata();
633 echo 'Dates updated';
634 exit;
635 }
636
637}
638
639new Crb_Syndication_Network();