Revive Old Posts – Auto Post to Social Media - Version 8.2.3

Version Description

  • 2019-04-10
Download this release

Release Info

Developer codeinwp
Plugin Icon 128x128 Revive Old Posts – Auto Post to Social Media
Version 8.2.3
Comparing to
See all releases

Code changes from version 8.2.2 to 8.2.3

CHANGELOG.md CHANGED
@@ -1,4 +1,11 @@
1
 
 
 
 
 
 
 
 
2
  ### v8.2.2 - 2019-03-20
3
  **Changes:**
4
  * New: Feedback button on plugin dashboard. Help us make ROP better by filling out the form!
1
 
2
+ ### v8.2.3 - 2019-04-10
3
+ **Changes:**
4
+ * New: Filter introduced for Post Title & Content separator (check revive.social docs)
5
+ * New: Known errors will now show a link to the fix in the log area
6
+ * Fix: Twitter images would not share for sites which moved to a different protocol but didn't update their image links in the database
7
+ * PRO Fix: Moved to LinkedIn API v2 (check revive.social docs)
8
+
9
  ### v8.2.2 - 2019-03-20
10
  **Changes:**
11
  * New: Feedback button on plugin dashboard. Help us make ROP better by filling out the form!
includes/admin/abstract/class-rop-services-abstract.php CHANGED
@@ -371,7 +371,7 @@ abstract class Rop_Services_Abstract {
371
  * Utility method to register a REST endpoint via WP.
372
  *
373
  * @since 8.0.0
374
- * @access public
375
  *
376
  * @param string $path The path for the endpoint.
377
  * @param string $callback The method name from the service class.
@@ -468,5 +468,46 @@ abstract class Rop_Services_Abstract {
468
  return $content;
469
  }
470
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
471
 
472
  }
371
  * Utility method to register a REST endpoint via WP.
372
  *
373
  * @since 8.0.0
374
+ * @access protected
375
  *
376
  * @param string $path The path for the endpoint.
377
  * @param string $callback The method name from the service class.
468
  return $content;
469
  }
470
 
471
+ /**
472
+ * Gets the appropriate documentation for errors in log.
473
+ *
474
+ * @since 8.2.3
475
+ * @access public
476
+ *
477
+ * @param string $response the API error response.
478
+ *
479
+ * @return string The document link.
480
+ */
481
+ protected function rop_get_error_docs( $response ) {
482
+
483
+ $errors_docs = array(
484
+ // Facebook errors
485
+ 'Only owners of the URL have the ability' => 'https://is.gd/fix_owners_url',
486
+ 'manage_pages and publish_pages as an admin' => 'https://is.gd/fix_manage_pages_error',
487
+ 'Invalid parameter' => 'https://is.gd/fix_link_issue',
488
+
489
+ // Twitter errors
490
+ 'Desktop applications only support the oauth_callback value' => 'https://is.gd/fix_oauth_callback_value',
491
+ 'User is over daily status update limit' => 'https://is.gd/fix_over_daily_limit',
492
+ 'Invalid media_id: Some' => 'https://is.gd/fix_invalid_media',
493
+
494
+ // LinkedIn errors
495
+ ''submitted-url' can not be empty' => 'https://is.gd/fix_link_issue',
496
+
497
+ // Add more common errors as necessary
498
+ );
499
+
500
+ foreach ( $errors_docs as $error => $link ) {
501
+
502
+ if ( strpos( $response, $error ) !== false ) {
503
+ $link = $link;
504
+ break;
505
+ }
506
+ }
507
+
508
+ return $this->logger->alert_error( 'This error is a known one. Please copy and paste the following link in your browser to see the solution: ' . $link );
509
+
510
+ }
511
+
512
 
513
  }
includes/admin/class-rop-admin.php CHANGED
@@ -663,4 +663,68 @@ class Rop_Admin {
663
  $cron->create_cron( false );
664
  }
665
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
666
  }
663
  $cron->create_cron( false );
664
  }
665
 
666
+ /**
667
+ * Linkedin API upgrade notice.
668
+ *
669
+ * @since 8.2.3
670
+ * @access public
671
+ */
672
+ public function rop_linkedin_api_v2_notice() {
673
+
674
+ if ( ! current_user_can( 'install_plugins' ) ) {
675
+ return;
676
+ }
677
+
678
+ // This option was introduced the same time we updated Linkedin API to v2.
679
+ // Gets created on plugin activation hook, old installs would not have this option.
680
+ // So we return in case this is a brand new install.
681
+ if ( ! empty( get_option( 'rop_first_install_version' ) ) ) {
682
+ return;
683
+ }
684
+
685
+ $user_id = get_current_user_id();
686
+
687
+ if ( get_user_meta( $user_id, 'rop-linkedin-api-notice-dismissed' ) ) {
688
+ return;
689
+ }
690
+
691
+ $services_model = new Rop_Services_Model();
692
+
693
+ $services = $services_model->get_authenticated_services();
694
+
695
+ foreach ( $services as $key => $value ) {
696
+
697
+ if ( $value['service'] == 'linkedin' ) {
698
+ $show_notice = true;
699
+ break;
700
+ }
701
+ }
702
+
703
+ if ( ! $show_notice ) {
704
+ return;
705
+ }
706
+
707
+ ?>
708
+ <div class="notice notice-error">
709
+ <?php echo sprintf( __( '%1$s%2$sRevive Old Posts:%3$s The Linkedin API Has been updated. You need to reconnect your LinkedIn account to continue posting to LinkedIn. Please see %4$sthis article for instructions.%5$s%6$s%7$s', 'tweet-old-post' ), '<p>', '<b>', '</b>', '<a href="https://docs.revive.social/article/1040-how-to-move-to-linkedin-api-v2" target="_blank">', '</a>', '<a style="float: right;" href="?rop-linkedin-api-notice-dismissed">Dismiss</a>', '</p>' ); ?>
710
+
711
+ </div>
712
+ <?php
713
+
714
+ }
715
+
716
+ /**
717
+ * Show Linkedin API upgrade notice.
718
+ *
719
+ * @since 8.2.3
720
+ * @access public
721
+ */
722
+ public function rop_show_linkedin_api_v2_notice() {
723
+ $user_id = get_current_user_id();
724
+ if ( isset( $_GET['rop-linkedin-api-notice-dismissed'] ) ) {
725
+ add_user_meta( $user_id, 'rop-linkedin-api-notice-dismissed', 'true', true );
726
+ }
727
+
728
+ }
729
+
730
  }
includes/admin/class-rop-global-settings.php CHANGED
@@ -169,7 +169,7 @@ class Rop_Global_Settings {
169
  'url_from_meta' => false,
170
  'url_meta_key' => '',
171
  'short_url' => true,
172
- 'short_url_service' => 'rviv.ly',
173
  'hashtags' => 'no-hashtags',
174
  'hashtags_length' => '20',
175
  'hashtags_common' => '',
@@ -189,7 +189,7 @@ class Rop_Global_Settings {
189
  'url_from_meta' => false,
190
  'url_meta_key' => '',
191
  'short_url' => true,
192
- 'short_url_service' => 'rviv.ly',
193
  'hashtags' => 'no-hashtags',
194
  'hashtags_length' => '10',
195
  'hashtags_common' => '',
@@ -210,7 +210,7 @@ class Rop_Global_Settings {
210
  'shortner_credentials' => array(),
211
  'url_meta_key' => '',
212
  'short_url' => true,
213
- 'short_url_service' => 'rviv.ly',
214
  'hashtags' => 'no-hashtags',
215
  'hashtags_length' => '10',
216
  'hashtags_common' => '',
@@ -230,7 +230,7 @@ class Rop_Global_Settings {
230
  'url_from_meta' => false,
231
  'url_meta_key' => '',
232
  'short_url' => true,
233
- 'short_url_service' => 'rviv.ly',
234
  'hashtags' => 'no-hashtags',
235
  'hashtags_length' => '10',
236
  'hashtags_common' => '',
@@ -249,7 +249,7 @@ class Rop_Global_Settings {
249
  'url_from_meta' => false,
250
  'url_meta_key' => '',
251
  'short_url' => true,
252
- 'short_url_service' => 'rviv.ly',
253
  'hashtags' => 'no-hashtags',
254
  'hashtags_length' => '20',
255
  'hashtags_common' => '',
169
  'url_from_meta' => false,
170
  'url_meta_key' => '',
171
  'short_url' => true,
172
+ 'short_url_service' => 'is.gd',
173
  'hashtags' => 'no-hashtags',
174
  'hashtags_length' => '20',
175
  'hashtags_common' => '',
189
  'url_from_meta' => false,
190
  'url_meta_key' => '',
191
  'short_url' => true,
192
+ 'short_url_service' => 'is.gd',
193
  'hashtags' => 'no-hashtags',
194
  'hashtags_length' => '10',
195
  'hashtags_common' => '',
210
  'shortner_credentials' => array(),
211
  'url_meta_key' => '',
212
  'short_url' => true,
213
+ 'short_url_service' => 'is.gd',
214
  'hashtags' => 'no-hashtags',
215
  'hashtags_length' => '10',
216
  'hashtags_common' => '',
230
  'url_from_meta' => false,
231
  'url_meta_key' => '',
232
  'short_url' => true,
233
+ 'short_url_service' => 'is.gd',
234
  'hashtags' => 'no-hashtags',
235
  'hashtags_length' => '10',
236
  'hashtags_common' => '',
249
  'url_from_meta' => false,
250
  'url_meta_key' => '',
251
  'short_url' => true,
252
+ 'short_url_service' => 'is.gd',
253
  'hashtags' => 'no-hashtags',
254
  'hashtags_length' => '20',
255
  'hashtags_common' => '',
includes/admin/class-rop-pointers.php CHANGED
@@ -376,7 +376,7 @@ class Rop_Pointers {
376
  */
377
  public function rop_enqueue_pointers() {
378
 
379
- if ( ! $screen = get_current_screen() ) {
380
  return;
381
  }
382
 
376
  */
377
  public function rop_enqueue_pointers() {
378
 
379
+ if ( ! $screen == get_current_screen() ) {
380
  return;
381
  }
382
 
includes/admin/helpers/class-rop-post-format-helper.php CHANGED
@@ -243,7 +243,7 @@ class Rop_Post_Format_Helper {
243
  $content = get_post_field( apply_filters( 'rop_content', 'post_content', $post_id ), $post_id );
244
  break;
245
  case 'post_title_content':
246
- $content = get_the_title( $post_id ) . ' ' . get_post_field( apply_filters( 'rop_content', 'post_content', $post_id ), $post_id );
247
  break;
248
  case 'custom_field':
249
  $content = $this->get_custom_field_value( $post_id, $this->post_format['custom_meta_field'] );
243
  $content = get_post_field( apply_filters( 'rop_content', 'post_content', $post_id ), $post_id );
244
  break;
245
  case 'post_title_content':
246
+ $content = get_the_title( $post_id ) . apply_filters( 'rop_title_content_separator', ' ' ) . get_post_field( apply_filters( 'rop_content', 'post_content', $post_id ), $post_id );
247
  break;
248
  case 'custom_field':
249
  $content = $this->get_custom_field_value( $post_id, $this->post_format['custom_meta_field'] );
includes/admin/services/class-rop-facebook-service.php CHANGED
@@ -349,7 +349,7 @@ class Rop_Facebook_Service extends Rop_Services_Abstract {
349
  $user_details['active'] = false;
350
  $pages_array[] = $user_details;
351
  }
352
- } while ( $pages = $api->next( $pages ) );
353
  } catch ( Exception $e ) {
354
  $this->logger->alert_error( 'Can not fetch pages for facebook. Error: ' . $e->getMessage() );
355
  }
@@ -598,10 +598,12 @@ class Rop_Facebook_Service extends Rop_Services_Abstract {
598
  return true;
599
  } catch ( Facebook\Exceptions\FacebookResponseException $e ) {
600
  $this->logger->alert_error( 'Unable to share post for facebook. Error: ' . $e->getMessage() );
 
601
 
602
  return false;
603
  } catch ( Facebook\Exceptions\FacebookSDKException $e ) {
604
  $this->logger->alert_error( 'Unable to share post for facebook. Error: ' . $e->getMessage() );
 
605
 
606
  return false;
607
  }
349
  $user_details['active'] = false;
350
  $pages_array[] = $user_details;
351
  }
352
+ } while ( $pages == $api->next( $pages ) );
353
  } catch ( Exception $e ) {
354
  $this->logger->alert_error( 'Can not fetch pages for facebook. Error: ' . $e->getMessage() );
355
  }
598
  return true;
599
  } catch ( Facebook\Exceptions\FacebookResponseException $e ) {
600
  $this->logger->alert_error( 'Unable to share post for facebook. Error: ' . $e->getMessage() );
601
+ $this->rop_get_error_docs( $e->getMessage() );
602
 
603
  return false;
604
  } catch ( Facebook\Exceptions\FacebookSDKException $e ) {
605
  $this->logger->alert_error( 'Unable to share post for facebook. Error: ' . $e->getMessage() );
606
+ $this->rop_get_error_docs( $e->getMessage() );
607
 
608
  return false;
609
  }
includes/admin/services/class-rop-linkedin-service.php CHANGED
@@ -43,8 +43,12 @@ class Rop_Linkedin_Service extends Rop_Services_Abstract {
43
  * @access protected
44
  * @var array $scopes The scopes to authorize with LinkedIn.
45
  */
46
- protected $scopes = array( 'r_basicprofile', 'r_emailaddress', 'rw_company_admin', 'w_share' );
47
-
 
 
 
 
48
 
49
  /**
50
  * Method to inject functionality into constructor.
@@ -142,6 +146,8 @@ class Rop_Linkedin_Service extends Rop_Services_Abstract {
142
  }
143
  $this->api = new \LinkedIn\Client( $this->strip_whitespace( $client_id ), $this->strip_whitespace( $client_secret ) );
144
 
 
 
145
  $this->api->setRedirectUrl( $this->get_legacy_url( 'linkedin' ) );
146
  }
147
 
@@ -219,12 +225,12 @@ class Rop_Linkedin_Service extends Rop_Services_Abstract {
219
  $api->setAccessToken( new LinkedIn\AccessToken( $args['token'] ) );
220
  try {
221
  $profile = $api->api(
222
- 'people/~:(id,email-address,first-name,last-name,formatted-name,picture-url)',
223
  array(),
224
  'GET'
225
  );
226
  } catch ( Exception $e ) {
227
- $this->logger->alert_error( 'Can not get linkedin user details. Error ' . $e->getMessage() );
228
  }
229
  if ( ! isset( $profile['id'] ) ) {
230
  return false;
@@ -245,7 +251,7 @@ class Rop_Linkedin_Service extends Rop_Services_Abstract {
245
  'private' => true,
246
  ),
247
  ),
248
- 'available_accounts' => $this->get_users( $profile ),
249
  );
250
 
251
  return true;
@@ -264,24 +270,46 @@ class Rop_Linkedin_Service extends Rop_Services_Abstract {
264
  *
265
  * @return array
266
  */
267
- private function get_users( $data = null ) {
268
  if ( empty( $data ) ) {
269
  return array();
270
  }
271
- $img = '';
272
- if ( isset( $data['pictureUrl'] ) && $data['pictureUrl'] ) {
273
- $img = $data['pictureUrl'];
 
 
 
 
 
 
274
  }
 
 
 
 
275
  $user_details = $this->user_default;
 
 
 
 
 
 
 
276
  $user_details['id'] = $this->strip_underscore( $data['id'] );
277
- $user_details['account'] = $this->normalize_string( $data['formattedName'] );
278
- $user_details['user'] = $this->normalize_string( $data['formattedName'] );
279
  $user_details['img'] = $img;
280
 
281
  $users = array( $user_details );
 
 
 
 
 
282
  try {
283
  $companies = $this->api->api(
284
- 'companies?format=json&is-company-admin=true',
285
  array(),
286
  'GET'
287
  );
@@ -291,20 +319,21 @@ class Rop_Linkedin_Service extends Rop_Services_Abstract {
291
  if ( empty( $companies ) ) {
292
  return $users;
293
  }
294
- if ( empty( $companies['values'] ) ) {
295
  return $users;
296
  }
297
- foreach ( $companies['values'] as $company ) {
298
  $users[] = wp_parse_args(
299
  array(
300
  'id' => $this->strip_underscore( $company['id'] ),
301
- 'account' => $company['name'],
302
  'is_company' => true,
303
- 'user' => $company['name'],
304
  ),
305
  $this->user_default
306
  );
307
  }
 
308
 
309
  return $users;
310
  }
@@ -381,7 +410,7 @@ class Rop_Linkedin_Service extends Rop_Services_Abstract {
381
  }
382
 
383
  /**
384
- * Method for publishing with Twitter service.
385
  *
386
  * @since 8.0.0
387
  * @access public
@@ -401,47 +430,26 @@ class Rop_Linkedin_Service extends Rop_Services_Abstract {
401
  $token = new \LinkedIn\AccessToken( $this->credentials['token'] );
402
  $api->setAccessToken( $token );
403
 
404
- $new_post = array(
405
- 'comment' => '',
406
- 'content' => array(
407
- 'title' => '',
408
- 'description' => '',
409
- 'submitted-url' => '',
410
- ),
411
- 'visibility' => array(
412
- 'code' => 'anyone',
413
- ),
414
- );
415
-
416
- if ( ! empty( $post_details['post_image'] ) ) {
417
- // If we have an video, share the placeholder, otherwise, share the image.
418
- if ( strpos( $post_details['mimetype']['type'], 'video' ) === false ) {
419
- $new_post['content']['submitted-image-url'] = $post_details['post_image'];
420
  } else {
421
- $new_post['content']['submitted-image-url'] = ROP_LITE_URL . 'assets/img/video_placeholder.jpg';
422
  }
 
 
 
 
 
 
423
  }
424
 
425
- $new_post['comment'] = $this->strip_excess_blank_lines( $post_details['content'] ) . $post_details['hashtags'];
426
- $new_post['content']['description'] = $post_details['content'];
427
- $new_post['content']['title'] = html_entity_decode( get_the_title( $post_details['post_id'] ) );
428
-
429
- $url_to_share = $this->get_url( $post_details );
430
- /**
431
- * If the url is not present, use the image instead in order for the share to be successful.
432
- */
433
- if ( empty( $url_to_share ) && ! empty( $post_details['post_image'] ) ) {
434
- $url_to_share = $post_details['post_image'];
435
- }
436
- $new_post['content']['submitted-url'] = $url_to_share;
437
-
438
- $new_post['visibility']['code'] = 'anyone';
439
-
440
  try {
441
  if ( isset( $args['is_company'] ) && $args['is_company'] === true ) {
442
  $api->post( sprintf( 'companies/%s/shares?format=json', $this->unstrip_underscore( $args['id'] ) ), $new_post );
443
  } else {
444
- $api->post( 'people/~/shares?format=json', $new_post );
445
  }
446
  $this->logger->alert_success(
447
  sprintf(
@@ -452,11 +460,162 @@ class Rop_Linkedin_Service extends Rop_Services_Abstract {
452
  )
453
  );
454
  } catch ( Exception $exception ) {
455
- $this->logger->alert_error( 'Can not share to linkedin. Error: ' . $exception->getMessage() );
 
456
 
457
  return false;
458
  }
459
 
460
  return true;
461
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
462
  }
43
  * @access protected
44
  * @var array $scopes The scopes to authorize with LinkedIn.
45
  */
46
+ protected $scopes = array( 'r_liteprofile', 'r_emailaddress', 'w_member_social');
47
+ // Company(organization) sharing scope cannot be used unless app approved for this scope.
48
+ // Added here for future reference
49
+ // https://stackoverflow.com/questions/54821731/in-linkedin-api-v2-0-how-to-get-company-list-by-persons-token
50
+ // https://business.linkedin.com/marketing-solutions/marketing-partners/become-a-partner/marketing-developer-program
51
+ // protected $scopes = array( 'r_liteprofile', 'r_emailaddress', 'w_member_social', , 'w_organization_social');
52
 
53
  /**
54
  * Method to inject functionality into constructor.
146
  }
147
  $this->api = new \LinkedIn\Client( $this->strip_whitespace( $client_id ), $this->strip_whitespace( $client_secret ) );
148
 
149
+ $this->api->setApiRoot( 'https://api.linkedin.com/v2/' );
150
+
151
  $this->api->setRedirectUrl( $this->get_legacy_url( 'linkedin' ) );
152
  }
153
 
225
  $api->setAccessToken( new LinkedIn\AccessToken( $args['token'] ) );
226
  try {
227
  $profile = $api->api(
228
+ 'me?projection=(id,firstName,lastName,profilePicture(displayImage~:playableStreams))',
229
  array(),
230
  'GET'
231
  );
232
  } catch ( Exception $e ) {
233
+ $this->logger->alert_error( 'Cannot get linkedin user details. Error ' . $e->getMessage() );
234
  }
235
  if ( ! isset( $profile['id'] ) ) {
236
  return false;
251
  'private' => true,
252
  ),
253
  ),
254
+ 'available_accounts' => $this->get_users( $profile, $api ),
255
  );
256
 
257
  return true;
270
  *
271
  * @return array
272
  */
273
+ private function get_users( $data = null, $api ) {
274
  if ( empty( $data ) ) {
275
  return array();
276
  }
277
+
278
+ try {
279
+ $email_array = $api->api(
280
+ 'emailAddress?q=members&projection=(elements*(handle~))',
281
+ array(),
282
+ 'GET'
283
+ );
284
+ } catch ( Exception $e ) {
285
+ $this->logger->alert_error( 'Cannot get linkedin user email. Error ' . $e->getMessage() );
286
  }
287
+
288
+ $email = $email_array['elements']['0']['handle~']['emailAddress'];
289
+
290
+ $img = $data['profilePicture']['displayImage~']['elements']['0']['identifiers']['0']['identifier'];
291
  $user_details = $this->user_default;
292
+
293
+ $fname_array = array_values( $data['firstName']['localized'] );
294
+ $firstname = $fname_array[0];
295
+
296
+ $lname_array = array_values( $data['lastName']['localized'] );
297
+ $lastname = $lname_array[0];
298
+
299
  $user_details['id'] = $this->strip_underscore( $data['id'] );
300
+ $user_details['account'] = $email;
301
+ $user_details['user'] = $firstname . ' ' . $lastname;
302
  $user_details['img'] = $img;
303
 
304
  $users = array( $user_details );
305
+
306
+ /*
307
+ We're not requesting the w_organization_social scope so code below wouldn't be used.
308
+ See scope property at the top of file for more details.
309
+
310
  try {
311
  $companies = $this->api->api(
312
+ 'organizationalEntityAcls?q=roleAssignee&role=ADMINISTRATOR&projection=(elements*(organizationalTarget~(localizedName,vanityName,logoV2)))',
313
  array(),
314
  'GET'
315
  );
319
  if ( empty( $companies ) ) {
320
  return $users;
321
  }
322
+ if ( empty( $companies['elements'] ) ) {
323
  return $users;
324
  }
325
+ foreach ( $companies['elements'] as $company ) {
326
  $users[] = wp_parse_args(
327
  array(
328
  'id' => $this->strip_underscore( $company['id'] ),
329
+ 'account' => $company['localizedName'],
330
  'is_company' => true,
331
+ 'user' => $company['localizedName'],
332
  ),
333
  $this->user_default
334
  );
335
  }
336
+ */
337
 
338
  return $users;
339
  }
410
  }
411
 
412
  /**
413
+ * Method for publishing with Linkedin service.
414
  *
415
  * @since 8.0.0
416
  * @access public
430
  $token = new \LinkedIn\AccessToken( $this->credentials['token'] );
431
  $api->setAccessToken( $token );
432
 
433
+ if ( get_post_type( $post_details['post_id'] ) !== 'attachment' ) {
434
+ // If post image option unchecked, share as article post
435
+ if ( empty( $post_details['post_image'] ) ) {
436
+ $new_post = $this->linkedin_article_post( $post_details, $args );
 
 
 
 
 
 
 
 
 
 
 
 
437
  } else {
438
+ $new_post = $this->linkedin_image_post( $post_details, $args, $token, $api );
439
  }
440
+ } elseif ( get_post_type( $post_details['post_id'] ) == 'attachment' ) {
441
+ // Linkedin Api v2 doesn't support video upload. Share as article post
442
+ if ( strpos( $post_details['mimetype']['type'], 'video' ) !== false ) {
443
+ $new_post = $this->linkedin_article_post( $post_details, $args );
444
+ }
445
+ $new_post = $this->linkedin_image_post( $post_details, $args, $token, $api );
446
  }
447
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
448
  try {
449
  if ( isset( $args['is_company'] ) && $args['is_company'] === true ) {
450
  $api->post( sprintf( 'companies/%s/shares?format=json', $this->unstrip_underscore( $args['id'] ) ), $new_post );
451
  } else {
452
+ $api->post( 'ugcPosts', $new_post );
453
  }
454
  $this->logger->alert_success(
455
  sprintf(
460
  )
461
  );
462
  } catch ( Exception $exception ) {
463
+ $this->logger->alert_error( 'Cannot share to linkedin. Error: ' . $exception->getMessage() );
464
+ $this->rop_get_error_docs( $exception->getMessage() );
465
 
466
  return false;
467
  }
468
 
469
  return true;
470
  }
471
+
472
+
473
+ /**
474
+ * Linkedin article post.
475
+ *
476
+ * @since 8.2.3
477
+ * @access private
478
+ *
479
+ * @param array $post_details The post details to be published by the service.
480
+ * @param array $args Arguments needed by the method.
481
+ *
482
+ * @return array
483
+ */
484
+ private function linkedin_article_post( $post_details, $args ) {
485
+
486
+ $new_post = array (
487
+ 'author' => 'urn:li:person:' . $args['id'],
488
+ 'lifecycleState' => 'PUBLISHED',
489
+ 'specificContent' =>
490
+ array (
491
+ 'com.linkedin.ugc.ShareContent' =>
492
+ array (
493
+ 'shareCommentary' =>
494
+ array (
495
+ 'text' => $this->strip_excess_blank_lines( $post_details['content'] ) . $post_details['hashtags'],
496
+ ),
497
+ 'shareMediaCategory' => 'ARTICLE',
498
+ 'media' =>
499
+ array (
500
+ 0 =>
501
+ array (
502
+ 'status' => 'READY',
503
+ 'description' =>
504
+ array (
505
+ 'text' => $this->strip_excess_blank_lines( $post_details['content'] ),
506
+ ),
507
+ 'originalUrl' => trim( $this->get_url( $post_details ) ),
508
+ 'title' =>
509
+ array (
510
+ 'text' => html_entity_decode( get_the_title( $post_details['post_id'] ) ),
511
+ ),
512
+ ),
513
+ ),
514
+ ),
515
+ ),
516
+ 'visibility' =>
517
+ array (
518
+ 'com.linkedin.ugc.MemberNetworkVisibility' => 'PUBLIC',
519
+ ),
520
+ );
521
+
522
+ return $new_post;
523
+
524
+ }
525
+
526
+ /**
527
+ * Linkedin image post format.
528
+ *
529
+ * @since 8.2.3
530
+ * @access private
531
+ *
532
+ * @param array $post_details The post details to be published by the service.
533
+ * @param array $args Arguments needed by the method.
534
+ * @param string $token The user token.
535
+ *
536
+ * @return array
537
+ */
538
+ private function linkedin_image_post( $post_details, $args, $token, $api ) {
539
+
540
+ $register_image = array (
541
+ 'registerUploadRequest' =>
542
+ array (
543
+ 'recipes' =>
544
+ array (
545
+ 0 => 'urn:li:digitalmediaRecipe:feedshare-image',
546
+ ),
547
+ 'owner' => 'urn:li:person:' . $args['id'],
548
+ 'serviceRelationships' =>
549
+ array (
550
+ 0 =>
551
+ array (
552
+ 'relationshipType' => 'OWNER',
553
+ 'identifier' => 'urn:li:userGeneratedContent',
554
+ ),
555
+ ),
556
+ ),
557
+ );
558
+
559
+ $response = $api->post( 'https://api.linkedin.com/v2/assets?action=registerUpload', $register_image );
560
+ $upload_url = $response['value']['uploadMechanism']['com.linkedin.digitalmedia.uploading.MediaUploadHttpRequest']['uploadUrl'];
561
+ $asset = $response['value']['asset'];
562
+
563
+ $img = wp_get_attachment_url( $post_details['post_id'] );
564
+
565
+ if ( ! class_exists( '\GuzzleHttp\Client' ) ) {
566
+ $this->logger->alert_error( 'Error: Cannot find Guzzle' );
567
+ return;
568
+ }
569
+ $guzzle = new \GuzzleHttp\Client();
570
+ $guzzle->request(
571
+ 'PUT',
572
+ $upload_url,
573
+ [
574
+ 'headers' => [
575
+ 'Authorization' => 'Bearer ' . $token,
576
+ ],
577
+ 'body' => fopen( $img, 'r' ),
578
+
579
+ ]
580
+ );
581
+
582
+ $new_post = array (
583
+ 'author' => 'urn:li:person:' . $args['id'],
584
+ 'lifecycleState' => 'PUBLISHED',
585
+ 'specificContent' =>
586
+ array (
587
+ 'com.linkedin.ugc.ShareContent' =>
588
+ array (
589
+ 'shareCommentary' =>
590
+ array (
591
+ 'text' => $this->strip_excess_blank_lines( $post_details['content'] ) . $this->get_url( $post_details ) . $post_details['hashtags'],
592
+ ),
593
+ 'shareMediaCategory' => 'IMAGE',
594
+ 'media' =>
595
+ array (
596
+ 0 =>
597
+ array (
598
+ 'status' => 'READY',
599
+ 'description' =>
600
+ array (
601
+ 'text' => html_entity_decode( get_the_title( $post_details['post_id'] ) ),
602
+ ),
603
+ 'media' => $asset,
604
+ 'title' =>
605
+ array (
606
+ 'text' => html_entity_decode( get_the_title( $post_details['post_id'] ) ),
607
+ ),
608
+ ),
609
+ ),
610
+ ),
611
+ ),
612
+ 'visibility' =>
613
+ array (
614
+ 'com.linkedin.ugc.MemberNetworkVisibility' => 'PUBLIC',
615
+ ),
616
+ );
617
+
618
+ return $new_post;
619
+ }
620
+
621
  }
includes/admin/services/class-rop-tumblr-service.php CHANGED
@@ -447,6 +447,7 @@ class Rop_Tumblr_Service extends Rop_Services_Abstract {
447
  );
448
  } catch ( Exception $exception ) {
449
  $this->logger->alert_error( 'Posting failed for Tumblr. Error: ' . $exception->getMessage() );
 
450
 
451
  return false;
452
  }
447
  );
448
  } catch ( Exception $exception ) {
449
  $this->logger->alert_error( 'Posting failed for Tumblr. Error: ' . $exception->getMessage() );
450
+ $this->rop_get_error_docs( $exception->getMessage() );
451
 
452
  return false;
453
  }
includes/admin/services/class-rop-twitter-service.php CHANGED
@@ -475,6 +475,7 @@ class Rop_Twitter_Service extends Rop_Services_Abstract {
475
  }
476
  } else {
477
  $this->logger->alert_error( sprintf( 'Can not upload media to twitter. Error: %s', json_encode( $media_response ) ) );
 
478
  }
479
  }
480
 
@@ -496,6 +497,7 @@ class Rop_Twitter_Service extends Rop_Services_Abstract {
496
  return true;
497
  } else {
498
  $this->logger->alert_error( sprintf( 'Error posting on twitter. Error: %s', json_encode( $response ) ) );
 
499
  }
500
 
501
  return false;
@@ -514,7 +516,11 @@ class Rop_Twitter_Service extends Rop_Services_Abstract {
514
 
515
  $dir = wp_upload_dir();
516
 
517
- if ( false === strpos( $image_url, $dir['baseurl'] . '/' ) ) {
 
 
 
 
518
  return $image_url;
519
  }
520
 
475
  }
476
  } else {
477
  $this->logger->alert_error( sprintf( 'Can not upload media to twitter. Error: %s', json_encode( $media_response ) ) );
478
+ $this->rop_get_error_docs( $media_response );
479
  }
480
  }
481
 
497
  return true;
498
  } else {
499
  $this->logger->alert_error( sprintf( 'Error posting on twitter. Error: %s', json_encode( $response ) ) );
500
+ $this->rop_get_error_docs( $response );
501
  }
502
 
503
  return false;
516
 
517
  $dir = wp_upload_dir();
518
 
519
+ $parsed = parse_url( $dir['baseurl'] );
520
+
521
+ $dir = $parsed['host'] . $parsed['path'];
522
+
523
+ if ( false === strpos( $image_url, $dir ) ) {
524
  return $image_url;
525
  }
526
 
includes/admin/shortners/class-rop-bitly-shortner.php CHANGED
@@ -43,7 +43,9 @@ class Rop_Bitly_Shortner extends Rop_Url_Shortner_Abstract {
43
  // if they are anything but identical, we should assume these need to be refreshed as this could be an upgrade to oauth2 keys.
44
  $prev = array_keys( $credentials );
45
  $now = array_keys( $this->credentials );
46
- if ( ! empty( $diff = array_diff( $prev, $now ) ) ) {
 
 
47
  return $this->credentials;
48
  }
49
  return $credentials;
43
  // if they are anything but identical, we should assume these need to be refreshed as this could be an upgrade to oauth2 keys.
44
  $prev = array_keys( $credentials );
45
  $now = array_keys( $this->credentials );
46
+ $diff = array_diff( $prev, $now );
47
+
48
+ if ( ! empty( $diff ) ) {
49
  return $this->credentials;
50
  }
51
  return $credentials;
includes/class-rop-activator.php CHANGED
@@ -30,6 +30,20 @@ class Rop_Activator {
30
  * @since 8.0.0
31
  * @access public
32
  */
33
- public static function activate() {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
34
 
35
  }
30
  * @since 8.0.0
31
  * @access public
32
  */
33
+ public static function activate() {
34
+
35
+ // Add version when ROP was first installed on site to DB.
36
+ $rop_first_install_version = get_option( 'rop_first_install_version' );
37
+
38
+ if ( class_exists( 'Rop' ) ) {
39
+ $rop = new Rop();
40
+ $version = $rop->get_version();
41
+ }
42
+
43
+ if ( empty( $rop_first_install_version ) && ! empty( $version ) ) {
44
+ add_option( 'rop_first_install_version', $version );
45
+ }
46
+
47
+ }
48
 
49
  }
includes/class-rop.php CHANGED
@@ -68,7 +68,7 @@ class Rop {
68
  public function __construct() {
69
 
70
  $this->plugin_name = 'rop';
71
- $this->version = '8.2.2';
72
 
73
  $this->load_dependencies();
74
  $this->set_locale();
@@ -128,6 +128,8 @@ class Rop {
128
  $tutorial_pointers = new Rop_Pointers();
129
 
130
  $this->loader->add_action( 'admin_init', $plugin_admin, 'legacy_auth', 2 );
 
 
131
  $this->loader->add_action( 'admin_notices', $plugin_admin, 'bitly_shortener_upgrade_notice' );
132
  $this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_styles' );
133
  $this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_scripts' );
68
  public function __construct() {
69
 
70
  $this->plugin_name = 'rop';
71
+ $this->version = '8.2.3';
72
 
73
  $this->load_dependencies();
74
  $this->set_locale();
128
  $tutorial_pointers = new Rop_Pointers();
129
 
130
  $this->loader->add_action( 'admin_init', $plugin_admin, 'legacy_auth', 2 );
131
+ $this->loader->add_action( 'admin_init', $plugin_admin, 'rop_show_linkedin_api_v2_notice' );
132
+ $this->loader->add_action( 'admin_notices', $plugin_admin, 'rop_linkedin_api_v2_notice' );
133
  $this->loader->add_action( 'admin_notices', $plugin_admin, 'bitly_shortener_upgrade_notice' );
134
  $this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_styles' );
135
  $this->loader->add_action( 'admin_enqueue_scripts', $plugin_admin, 'enqueue_scripts' );
readme.md CHANGED
@@ -134,6 +134,14 @@ http://revive.social/plugins/revive-old-post
134
 
135
 
136
  ## Changelog ##
 
 
 
 
 
 
 
 
137
  ### 8.2.2 - 2019-03-20 ###
138
 
139
  * New: Feedback button on plugin dashboard. Help us make ROP better by filling out the form!
134
 
135
 
136
  ## Changelog ##
137
+ ### 8.2.3 - 2019-04-10 ###
138
+
139
+ * New: Filter introduced for Post Title & Content separator (check revive.social docs)
140
+ * New: Known errors will now show a link to the fix in the log area
141
+ * Fix: Twitter images would not share for sites which moved to a different protocol but didn't update their image links in the database
142
+ * PRO Fix: Moved to LinkedIn API v2 (check revive.social docs)
143
+
144
+
145
  ### 8.2.2 - 2019-03-20 ###
146
 
147
  * New: Feedback button on plugin dashboard. Help us make ROP better by filling out the form!
readme.txt CHANGED
@@ -134,6 +134,14 @@ http://revive.social/plugins/revive-old-post
134
 
135
 
136
  == Changelog ==
 
 
 
 
 
 
 
 
137
  = 8.2.2 - 2019-03-20 =
138
 
139
  * New: Feedback button on plugin dashboard. Help us make ROP better by filling out the form!
134
 
135
 
136
  == Changelog ==
137
+ = 8.2.3 - 2019-04-10 =
138
+
139
+ * New: Filter introduced for Post Title & Content separator (check revive.social docs)
140
+ * New: Known errors will now show a link to the fix in the log area
141
+ * Fix: Twitter images would not share for sites which moved to a different protocol but didn't update their image links in the database
142
+ * PRO Fix: Moved to LinkedIn API v2 (check revive.social docs)
143
+
144
+
145
  = 8.2.2 - 2019-03-20 =
146
 
147
  * New: Feedback button on plugin dashboard. Help us make ROP better by filling out the form!
themeisle-hash.json CHANGED
@@ -1 +1 @@
1
- {"class-rop-autoloader.php":"7bfbb1554230d0ace777adb2e42bebeb","index.php":"39ab8276fb0e4bd3fcab3270822c5977","tweet-old-post.php":"dd0d94a2c177b20cd9e130a1d6112226","uninstall.php":"d68464155ef595362c4cd0f47488d77c"}
1
+ {"class-rop-autoloader.php":"7bfbb1554230d0ace777adb2e42bebeb","index.php":"39ab8276fb0e4bd3fcab3270822c5977","tweet-old-post.php":"9a821bef3c0b3d425704fa9e106e86e9","uninstall.php":"5c68258039522cc06b0cf2108e735c4f"}
tweet-old-post.php CHANGED
@@ -16,7 +16,7 @@
16
  * Plugin Name: Revive Old Posts
17
  * Plugin URI: https://revive.social/
18
  * Description: WordPress plugin that helps you to keeps your old posts alive by sharing them and driving more traffic to them from twitter/facebook or linkedin. It also helps you to promote your content. You can set time and no of posts to share to drive more traffic.For questions, comments, or feature requests, <a href="http://revive.social/support/?utm_source=plugindesc&utm_medium=announce&utm_campaign=top">contact </a> us!
19
- * Version: 8.2.2
20
  * Author: revive.social
21
  * Author URI: https://revive.social/
22
  * Requires at least: 3.5
@@ -98,7 +98,7 @@ function run_rop() {
98
  }
99
 
100
  define( 'ROP_PRO_URL', 'http://revive.social/plugins/revive-old-post/' );
101
- define( 'ROP_LITE_VERSION', '8.2.2' );
102
  define( 'ROP_LITE_BASE_FILE', __FILE__ );
103
  define( 'ROP_DEBUG', false );
104
  define( 'ROP_LITE_PATH', plugin_dir_path( __FILE__ ) );
16
  * Plugin Name: Revive Old Posts
17
  * Plugin URI: https://revive.social/
18
  * Description: WordPress plugin that helps you to keeps your old posts alive by sharing them and driving more traffic to them from twitter/facebook or linkedin. It also helps you to promote your content. You can set time and no of posts to share to drive more traffic.For questions, comments, or feature requests, <a href="http://revive.social/support/?utm_source=plugindesc&utm_medium=announce&utm_campaign=top">contact </a> us!
19
+ * Version: 8.2.3
20
  * Author: revive.social
21
  * Author URI: https://revive.social/
22
  * Requires at least: 3.5
98
  }
99
 
100
  define( 'ROP_PRO_URL', 'http://revive.social/plugins/revive-old-post/' );
101
+ define( 'ROP_LITE_VERSION', '8.2.3' );
102
  define( 'ROP_LITE_BASE_FILE', __FILE__ );
103
  define( 'ROP_DEBUG', false );
104
  define( 'ROP_LITE_PATH', plugin_dir_path( __FILE__ ) );
uninstall.php CHANGED
@@ -56,6 +56,7 @@ if ( isset( $housekeeping ) && $housekeeping ) {
56
  }
57
 
58
  delete_metadata( 'user', 0, 'rop_publish_now_notice_dismissed', '', true );
 
59
 
60
  global $wpdb;
61
  $post_meta = $wpdb->prefix . 'postmeta';
56
  }
57
 
58
  delete_metadata( 'user', 0, 'rop_publish_now_notice_dismissed', '', true );
59
+ delete_metadata( 'user', 0, 'rop_first_install_version', '', true );
60
 
61
  global $wpdb;
62
  $post_meta = $wpdb->prefix . 'postmeta';
vendor/autoload.php CHANGED
@@ -4,4 +4,4 @@
4
 
5
  require_once __DIR__ . '/composer' . '/autoload_real.php';
6
 
7
- return ComposerAutoloaderInit6a03a045a8bbf0a818df1f6e63ddc212::getLoader();
4
 
5
  require_once __DIR__ . '/composer' . '/autoload_real.php';
6
 
7
+ return ComposerAutoloaderInite35032fbb4485eaaabd5e24b6e41609f::getLoader();
vendor/composer/autoload_real.php CHANGED
@@ -2,7 +2,7 @@
2
 
3
  // autoload_real.php @generated by Composer
4
 
5
- class ComposerAutoloaderInit6a03a045a8bbf0a818df1f6e63ddc212
6
  {
7
  private static $loader;
8
 
@@ -19,9 +19,9 @@ class ComposerAutoloaderInit6a03a045a8bbf0a818df1f6e63ddc212
19
  return self::$loader;
20
  }
21
 
22
- spl_autoload_register(array('ComposerAutoloaderInit6a03a045a8bbf0a818df1f6e63ddc212', 'loadClassLoader'), true, true);
23
  self::$loader = $loader = new \Composer\Autoload\ClassLoader();
24
- spl_autoload_unregister(array('ComposerAutoloaderInit6a03a045a8bbf0a818df1f6e63ddc212', 'loadClassLoader'));
25
 
26
  $map = require __DIR__ . '/autoload_namespaces.php';
27
  foreach ($map as $namespace => $path) {
@@ -42,14 +42,14 @@ class ComposerAutoloaderInit6a03a045a8bbf0a818df1f6e63ddc212
42
 
43
  $includeFiles = require __DIR__ . '/autoload_files.php';
44
  foreach ($includeFiles as $fileIdentifier => $file) {
45
- composerRequire6a03a045a8bbf0a818df1f6e63ddc212($fileIdentifier, $file);
46
  }
47
 
48
  return $loader;
49
  }
50
  }
51
 
52
- function composerRequire6a03a045a8bbf0a818df1f6e63ddc212($fileIdentifier, $file)
53
  {
54
  if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
55
  require $file;
2
 
3
  // autoload_real.php @generated by Composer
4
 
5
+ class ComposerAutoloaderInite35032fbb4485eaaabd5e24b6e41609f
6
  {
7
  private static $loader;
8
 
19
  return self::$loader;
20
  }
21
 
22
+ spl_autoload_register(array('ComposerAutoloaderInite35032fbb4485eaaabd5e24b6e41609f', 'loadClassLoader'), true, true);
23
  self::$loader = $loader = new \Composer\Autoload\ClassLoader();
24
+ spl_autoload_unregister(array('ComposerAutoloaderInite35032fbb4485eaaabd5e24b6e41609f', 'loadClassLoader'));
25
 
26
  $map = require __DIR__ . '/autoload_namespaces.php';
27
  foreach ($map as $namespace => $path) {
42
 
43
  $includeFiles = require __DIR__ . '/autoload_files.php';
44
  foreach ($includeFiles as $fileIdentifier => $file) {
45
+ composerRequiree35032fbb4485eaaabd5e24b6e41609f($fileIdentifier, $file);
46
  }
47
 
48
  return $loader;
49
  }
50
  }
51
 
52
+ function composerRequiree35032fbb4485eaaabd5e24b6e41609f($fileIdentifier, $file)
53
  {
54
  if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) {
55
  require $file;