8 changes: 4 additions & 4 deletions assets/js/build/publish_now.min.js

Large diffs are not rendered by default.

21 changes: 6 additions & 15 deletions includes/admin/abstract/class-rop-services-abstract.php
Expand Up @@ -332,6 +332,8 @@ protected abstract function request_api_token();
/**
* Method to generate url for service post share.
*
* Apply short url if available.
*
* @param array $post_details The post details to be published by the service.
*
* @return string
Expand All @@ -340,28 +342,17 @@ protected abstract function request_api_token();
*/
protected function get_url( $post_details ) {

$link = ( ! empty( $post_details['post_url'] ) ) ? ' ' . $post_details['post_url'] : '';

if ( empty( $link ) ) {
if ( empty( $post_details['post_url'] ) ) {
return '';
}

if ( ! $post_details['short_url'] ) {
return $link;
}
if ( empty( $post_details['short_url_service'] ) ) {
return $link;
if ( empty( $post_details['short_url'] ) || empty( $post_details['short_url_service'] || 'wp_short_url' === $post_details['short_url_service'] ) ) {
return ' ' . $post_details['post_url'];
}

$post_format_helper = new Rop_Post_Format_Helper();

if ( $post_details['short_url_service'] === 'wp_short_url' ) {
return $link;
}

$link = ' ' . $post_format_helper->get_short_url( $post_details['post_url'], $post_details['short_url_service'], $post_details['shortner_credentials'] );

return $link;
return ' ' . $post_format_helper->get_short_url( $post_details['post_url'], $post_details['short_url_service'], $post_details['shortner_credentials'] );
}

/**
Expand Down
48 changes: 29 additions & 19 deletions includes/admin/class-rop-admin.php
Expand Up @@ -321,7 +321,16 @@ public function enqueue_scripts() {
$added_services = $services->get_authenticated_services();
$added_networks = 0;
if ( $added_services ) {
$added_networks = count( array_unique( wp_list_pluck( array_values( $added_services ), 'service' ) ) );

$uniq_auth_accounts = array();

foreach ( $added_services as $key => $service ) {
if ( isset( $service['service'] ) && ! in_array( $service['service'], $uniq_auth_accounts, true ) ) {
$uniq_auth_accounts[] = $service['service'];
}
}

$added_networks = count( $uniq_auth_accounts );
}

$global_settings = new Rop_Global_Settings();
Expand Down Expand Up @@ -370,6 +379,7 @@ public function enqueue_scripts() {
'authToken' => $token,
'adminUrl' => urlencode( $admin_url ),
'authSignature' => $signature,
'pluginVersion' => ROP_LITE_VERSION,
);

if ( 'publish_now' === $page ) {
Expand Down Expand Up @@ -785,8 +795,9 @@ public function add_publish_actions() {
public function publish_now_attributes( $default ) {
global $post;

if ( 'publish' === $post->post_status ) {
$default['action'] = 'yes' === get_post_meta( $post->ID, 'rop_publish_now', true );
if ( in_array( $post->post_status, array( 'future', 'publish' ), true ) ) {
$default['action'] = 'yes' === get_post_meta( $post->ID, 'rop_publish_now', true );
$default['instant_share_by_default'] = $default['action'];
}
$default['active'] = get_post_meta( $post->ID, 'rop_publish_now_accounts', true );

Expand All @@ -799,30 +810,29 @@ public function publish_now_attributes( $default ) {
* @param int $post_id The post ID.
*/
public function maybe_publish_now( $post_id ) {
if ( ! isset( $_POST['rop_publish_now_nonce'] ) || ! wp_verify_nonce( $_POST['rop_publish_now_nonce'], 'rop_publish_now_nonce' ) ) {
if ( empty( $_POST['rop_publish_now_nonce'] ) ) {
return;
}

if ( empty( $_POST['publish_now_accounts'] ) ) {
if ( ! wp_verify_nonce( $_POST['rop_publish_now_nonce'], 'rop_publish_now_nonce' ) ) {
return;
}

if ( get_post_status( $post_id ) !== 'publish' ) {
if ( empty( $_POST['publish_now_accounts'] ) || empty( $_POST['publish_now'] ) ) {
delete_post_meta( $post_id, 'rop_publish_now' );
delete_post_meta( $post_id, 'rop_publish_now_accounts' );
return;
}

if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
$post_status = get_post_status( $post_id );
if ( ! in_array( $post_status, array( 'future', 'publish' ), true ) ) {
return;
}

if ( ! current_user_can( 'edit_post', $post_id ) ) {
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
return;
}

if ( ! isset( $_POST['publish_now'] ) || empty( $_POST['publish_now'] ) ) {
delete_post_meta( $post_id, 'rop_publish_now' );
delete_post_meta( $post_id, 'rop_publish_now_accounts' );

if ( ! current_user_can( 'edit_post', $post_id ) ) {
return;
}

Expand All @@ -835,7 +845,7 @@ public function maybe_publish_now( $post_id ) {
$services = new Rop_Services_Model();
$settings = new Rop_Settings_Model();

$active = array_keys( $services->get_active_accounts() );
$active = array_keys( $services->get_active_accounts() );
// has something been added extra?
$extra = array_diff( $enabled, $active );
// reject the extra.
Expand All @@ -844,19 +854,19 @@ public function maybe_publish_now( $post_id ) {
$instant_share_custom_content = array();

foreach ( $enabled as $account_id ) {
$custom_message = ! empty( $_POST[ $account_id ] ) ? $_POST[ $account_id ] : '';
$instant_share_custom_content[ $account_id ] = $custom_message;
$custom_message = ! empty( $_POST[ $account_id ] ) ? $_POST[ $account_id ] : '';
$instant_share_custom_content[ $account_id ] = $custom_message;
}

update_post_meta( $post_id, 'rop_publish_now', 'yes' );
update_post_meta( $post_id, 'rop_publish_now_accounts', $instant_share_custom_content );

// If user wants to run this operation on page refresh instead of via Cron.
if ( $settings->get_true_instant_share() ) {
$this->rop_cron_job_publish_now( $post_id, $instant_share_custom_content );
return;
}

update_post_meta( $post_id, 'rop_publish_now', 'yes' );
update_post_meta( $post_id, 'rop_publish_now_accounts', $instant_share_custom_content );

if ( empty( $enabled ) ) {
return;
}
Expand Down
8 changes: 7 additions & 1 deletion includes/admin/class-rop-rest-api.php
Expand Up @@ -833,6 +833,8 @@ private function remove_service( $data ) {
/**
* API method called to retrieve a service sign in url.
*
* Used to create an authentication url for users who want to use their own app via personal auth keys/tokens.
*
* @SuppressWarnings(PHPMD.UnusedPrivateMethod) As it is called dynamically.
* @Throws Exception Throws an exception if the service can't be built.
*
Expand Down Expand Up @@ -1060,7 +1062,11 @@ private function add_account_tw( $data ) {
$model = new Rop_Services_Model();
$db = new Rop_Db_Upgrade();

$twitter_service->add_account_with_app( $data );
if ( ! empty( $data['pages'] ) && ! empty( $data['pages']['credentials']['rop_auth_token'] ) ) {
$twitter_service->add_account_from_rop_server( $data );
} else {
$twitter_service->add_account_with_app( $data );
}

$services[ $twitter_service->get_service_id() ] = $twitter_service->get_service();
$active_accounts = array_merge( $active_accounts, $twitter_service->get_service_active_accounts() );
Expand Down
238 changes: 210 additions & 28 deletions includes/admin/services/class-rop-twitter-service.php
Expand Up @@ -127,7 +127,7 @@ public function authorize() {
* @param string $oauth_token The OAuth Token. Default empty.
* @param string $oauth_token_secret The OAuth Token Secret. Default empty.
*
* @return mixed
* @return \Abraham\TwitterOAuth\TwitterOAuth
*/
public function get_api( $oauth_token = '', $oauth_token_secret = '' ) {
if ( $this->api == null ) {
Expand Down Expand Up @@ -288,6 +288,8 @@ public function set_credentials( $args ) {
private function get_users( $data = null ) {
// assign default values to variable
$user = $this->user_default;

// Check credentials if the user is using his own dev account.
if ( $data == null ) {
$this->set_api( $this->credentials['oauth_token'], $this->credentials['oauth_token_secret'], $this->consumer_key, $this->consumer_secret );
$api = $this->get_api();
Expand Down Expand Up @@ -573,29 +575,27 @@ public function share( $post_details, $args = array() ) {
return false;
}

$is_rop_app = get_option( 'rop_twitter_via_rs_app', 'no' );
if ( 'yes' === $is_rop_app ) {
$check_sharing_limit = Rop_Admin::rop_check_reached_sharing_limit( 'tw' );
if ( $check_sharing_limit && ! $check_sharing_limit->is_valid_license ) {
$this->logger->alert_error( sprintf( 'Error posting on twitter. Error: %s', Rop_I18n::get_labels( 'sharing.invalid_license' ) ) );
return false;
}
$share_via_rop_server = ! empty( $this->credentials['rop_auth_token'] );

if ( $check_sharing_limit && ! $check_sharing_limit->is_valid ) {
$error_message = sprintf( Rop_I18n::get_labels( 'sharing.reached_sharing_limit' ), $check_sharing_limit->limit );
$this->logger->alert_error( sprintf( 'Error posting on twitter. Error: %s', $error_message ) );
return false;
}
$transient_key = 'rop_twitter_limit_reset_' . wp_hash( $share_via_rop_server ? $this->credentials['rop_auth_token'] : $this->credentials['oauth_token'] );
$limit_saved_msg = get_transient( $transient_key );

if ( ! empty( $limit_saved_msg ) ) {
$this->logger->alert_error( $limit_saved_msg );
return false;
}

$this->set_api(
$this->credentials['oauth_token'],
$this->credentials['oauth_token_secret'],
isset( $this->credentials['consumer_key'] ) ? $this->credentials['consumer_key'] : '',
isset( $this->credentials['consumer_secret'] ) ? $this->credentials['consumer_secret'] : ''
);
$api = $this->get_api();
$new_post = array();
$api = null;
if ( ! $share_via_rop_server ) {
$this->set_api(
$this->credentials['oauth_token'],
$this->credentials['oauth_token_secret'],
isset( $this->credentials['consumer_key'] ) ? $this->credentials['consumer_key'] : '',
isset( $this->credentials['consumer_secret'] ) ? $this->credentials['consumer_secret'] : ''
);
$api = $this->get_api();
}

$post_id = $post_details['post_id'];
$post_url = $post_details['post_url'];
Expand All @@ -612,8 +612,10 @@ public function share( $post_details, $args = array() ) {
}

// Twitter media post
if ( ! empty( $share_as_image_post ) || get_post_type( $post_id ) === 'attachment' ) {
if ( isset( $api ) && ! empty( $share_as_image_post ) || get_post_type( $post_id ) === 'attachment' ) {
$new_post = $this->twitter_media_post( $post_details, $api );
} elseif ( ! isset( $api ) && ! empty( $share_as_image_post ) ) {
$this->logger->info( __( 'Post with image is available only the local mode (Use my own API Keys). You can find the option when adding your X account to the plugin Dashboard.', 'tweet-old-post' ) . ' ' . __( ' Read more on:', 'tweet-old-post' ) . 'https://docs.revive.social/article/1908-how-to-solve-453-twitter-error-in-rop' );
}

if ( empty( $new_post ) ) {
Expand All @@ -634,10 +636,97 @@ public function share( $post_details, $args = array() ) {

$this->logger->info( sprintf( 'Before twitter share: %s', json_encode( $new_post ) ) );

$api->setApiVersion( '2' );
$response = $api->post( 'tweets', $new_post, true );
$response = array();
$response_headers = array();
$server_response = array();

if ( ! $share_via_rop_server ) {
$api->setApiVersion( '2' ); // Note: Make sure to always set the correct API version before making a request.
$response = $api->post( 'tweets', $new_post, true );
$response_headers = $api->getLastXHeaders();

if ( isset( $response->data->id ) ) {
$this->logger->info( sprintf( '[X API] Response: %s', json_encode( $response_headers ) ) );

$response = (array) $response;
if ( ! empty( $response['data'] ) ) {
$response['data'] = (array) $response['data'];
}
} else {
$response = $this->rop_share_post_via_server( 'tw', $new_post, $this->credentials['rop_auth_token'] );

$this->logger->info( sprintf( '[Revive Social] Response: %s', json_encode( $response_headers ) ) );

$body = wp_remote_retrieve_body( $response );
$body = json_decode( $body, true );

if ( ! empty( $body ) ) {

if ( ! empty( $body['server'] ) ) {
$server_response = $body['server'];

// If we have a cached response, use it to apply the logic for rate limiting.
if ( ! empty( $server_response['cached_response'] ) ) {
$body = $server_response['cached_response'];
}
}

if ( ! empty( $body['api_headers'] ) ) {
$response_headers = $body['api_headers'];
}

if ( ! empty( $body['api_body'] ) ) {
$response = $body['api_body'];
}
}
}

$limit_remaining = isset( $response_headers['x_rate_limit_remaining'] ) ? $response_headers['x_rate_limit_remaining'] : false;
$user_24h_limit_remaining = isset( $response_headers['x_user_limit_24hour_remaining'] ) ? $response_headers['x_user_limit_24hour_remaining'] : false;
$app_24h_limit_remaining = isset( $response_headers['x_app_limit_24hour_remaining'] ) ? $response_headers['x_app_limit_24hour_remaining'] : false;

$reset_time_msg = '';
$time_diff = 0;
$max_reset = 0;
$log_limit_msg = __( 'X posting limit reached. Sharing on X will be skipped.', 'tweet-old-post' ) . ' (' . __( 'Learn more about X limits at', 'tweet-old-post' ) . ' https://developer.twitter.com/en/docs/twitter-api/rate-limits). ';

if ( false !== $limit_remaining && $limit_remaining <= 0 ) {
$reset = isset( $response_headers['x_rate_limit_reset'] ) ? $response_headers['x_rate_limit_reset'] : false; // in UTC epoch seconds

if ( $reset ) {
$time_diff = max( $time_diff, $reset - time() );
$max_reset = max( $max_reset, $reset );

$reset_time_msg .= '(' . __( '"x-rate-limit-remaining" will reset at:', 'tweet-old-post' ) . ' ' . date( 'Y-m-d H:i:s', $reset ) . ' UTC' . ')';
}
}

if ( false !== $user_24h_limit_remaining && $user_24h_limit_remaining <= 0 ) {
$reset = isset( $response_headers['x_user_limit_24hour_reset'] ) ? $response_headers['x_user_limit_24hour_reset'] : false;

if ( $reset ) {
$time_diff = max( $time_diff, $reset - time() );
$max_reset = max( $max_reset, $reset );

$reset_time_msg .= '(' . __( '"x-user-limit-24hour-remaining" will reset at:', 'tweet-old-post' ) . ' ' . date( 'Y-m-d H:i:s', $reset ) . ' UTC' . ')';
}
}

if ( false !== $app_24h_limit_remaining && $app_24h_limit_remaining <= 0 ) {
$reset = isset( $response_headers['x_app_limit_24hour_reset'] ) ? $response_headers['x_app_limit_24hour_reset'] : false;

if ( $reset ) {
$time_diff = max( $time_diff, $reset - time() );
$max_reset = max( $max_reset, $reset );

$reset_time_msg .= '(' . __( '"x-app-limit-24hour-remaining" will reset at:', 'tweet-old-post' ) . ' ' . date( 'Y-m-d H:i:s', $reset ) . ' UTC' . ')';
}
}

if ( 0 < $time_diff ) {
set_transient( $transient_key, $log_limit_msg . __( 'All limits will be fully reset by', 'tweet-old-post' ) . ': ' . date( 'Y-m-d H:i:s', $max_reset ) . ' ' . $reset_time_msg, $time_diff );
}

if ( isset( $response['data'] ) && ! empty( $response['data']['id'] ) ) {
$this->logger->alert_success(
sprintf(
'Successfully shared %s to %s on %s ',
Expand All @@ -648,12 +737,31 @@ public function share( $post_details, $args = array() ) {
);

return true;
} else {
$this->logger->alert_error( sprintf( 'Error posting on twitter. Error: %s', json_encode( $response ) ) );
$this->rop_get_error_docs( $response );
return false;
}

$msg = 'Invalid response from X server.';
$extra = $response;

if ( isset( $response['detail'] ) ) {
$msg = $response['detail'];
}

if ( ! empty( $server_response['message'] ) ) {
$msg = $server_response['message'];

if ( 'limit_reached' === $server_response['code'] ) {
$extra = json_encode( $response_headers );
}

if ( empty( $extra ) ) {
$extra = $server_response['code'];
}
}

$this->logger->alert_error( sprintf( 'Error posting on X: %s | Additional info: %s', $msg, json_encode( $extra ) ) );
$this->rop_get_error_docs( $response );

return false;
}

/**
Expand Down Expand Up @@ -724,6 +832,44 @@ public function add_account_with_app( $account_data ) {
return true;
}

/**
* This method will load and prepare the account data for Twitter user using the info from the Rop server.
*
* @since 8.4.0
*
* @param array $account_data Twitter pages data.
*
* @return bool
*/
public function add_account_from_rop_server( $account_data ) {
if ( ! $this->is_set_not_empty( $account_data, array( 'id' ) ) ) {
return false;
}
$the_id = $account_data['id'];
$account_data = $account_data['pages'];

$this->service = array(
'id' => $the_id,
'service' => $this->service_name,
'credentials' => $account_data['credentials'],
'public_credentials' => array(
'consumer_key' => array(
'name' => 'API Key',
'value' => '',
'private' => false,
),
'consumer_secret' => array(
'name' => 'API secret key',
'value' => '',
'private' => true,
),
),
'available_accounts' => $this->get_users( (object) $account_data ),
);

return true;
}

/**
* Method to populate additional data.
*
Expand All @@ -736,4 +882,40 @@ public function populate_additional_data( $account ) {
return $account;
}

/**
* Send the post to RoP server for sharing
*
* @param string $sharing_type Post sharing type.
* @return array|WP_Error
*/
public static function rop_share_post_via_server( $sharing_type = 'tw', $post_data, $rop_auth_token ) {
$license_key = 'free';
$plan_id = 0;
if ( 'valid' === apply_filters( 'product_rop_license_status', 'invalid' ) ) {
$license_key = apply_filters( 'product_rop_license_key', 'free' );
$plan_id = apply_filters( 'product_rop_license_plan', 0 );
}
// Send API request.
$response = wp_remote_post(
ROP_POST_ON_X_API,
apply_filters(
'rop_post_sharing_api_args',
array(
'timeout' => 100,
'body' => array_merge(
array(
'sharing_type' => $sharing_type,
'license' => $license_key,
'plan_id' => $plan_id,
'site_url' => get_site_url(),
'post_data' => $post_data,
'rop_auth_token' => $rop_auth_token,
)
),
)
)
);

return $response;
}
}
13 changes: 10 additions & 3 deletions includes/admin/shortners/class-rop-isgd-shortner.php
Expand Up @@ -40,15 +40,22 @@ public function init() {
public function shorten_url( $url ) {

$response = $this->callAPI(
'https://is.gd/api.php',
'https://is.gd/create.php',
array( 'method' => 'get' ),
array( 'longurl' => $url),
array(
'format' => 'simple',
'url' => $url,
),
null
);

$shortURL = $url;
if ( intval( $response['error'] ) == 200 ) {
if ( intval( $response['error'] ) == 200 && wp_http_validate_url( $response['response'] ) ) {
$shortURL = $response['response'];
} else {
$this->error->throw_exception( 'Error', 'is.gd error: ' . print_r( $response, true ) );
}

return $shortURL;
}
}
10 changes: 6 additions & 4 deletions includes/class-rop-i18n.php
Expand Up @@ -72,7 +72,7 @@ public function load_service_locals( $services ) {
* @return array|mixed|string String localized
*/
public static function get_labels( $key = '' ) {
$tw_new_name = __( 'X (ex Twitter)', 'tweet-old-post' );
$tw_new_name = __( 'X (Twitter)', 'tweet-old-post' );
$labels = array(
'accounts' => array(
'menu_item' => __( 'Accounts', 'tweet-old-post' ),
Expand Down Expand Up @@ -282,6 +282,7 @@ public static function get_labels( $key = '' ) {
'instagram_disable_link_recommendation' => sprintf( __( '%1$sNote:%2$s We recommend that you disable links for Instagram posts. If you do leave this option checked, then we recommend that you enable a shortener.', 'tweet-old-post' ), '<strong>', '</strong>' ),
'instagram_image_post_default' => sprintf( __( '%1$sNote:%2$s Instagram posts need to be an image.', 'tweet-old-post' ), '<strong>', '</strong>' ),
'vk_unsupported_shorteners' => sprintf( __( '%1$sNote:%2$s is.gd shortener is not currently supported by VK.com.', 'tweet-old-post' ), '<strong>', '</strong>' ),
'not_available_with_rop_server' => __( 'This feature is not available for X accounts authorized via Revival Social.', 'tweet-old-post' ),
),
'schedule' => array(
'menu_item' => __( 'Custom Schedule', 'tweet-old-post' ),
Expand Down Expand Up @@ -326,9 +327,10 @@ public static function get_labels( $key = '' ) {

),
'logs' => array(
'menu_item' => __( 'Sharing Logs', 'tweet-old-post' ),
'clear_btn' => __( 'Clear logs', 'tweet-old-post' ),
'no_logs' => __( 'No recent logs!', 'tweet-old-post' ),
'menu_item' => __( 'Sharing Logs', 'tweet-old-post' ),
'clear_btn' => __( 'Clear logs', 'tweet-old-post' ),
'no_logs' => __( 'No recent logs!', 'tweet-old-post' ),
'export_btn' => __( 'Export logs', 'tweet-old-post' ),
),
'general' => array(
'plugin_name' => __( 'Revive Old Posts', 'tweet-old-post' ),
Expand Down
2 changes: 1 addition & 1 deletion includes/class-rop.php
Expand Up @@ -68,7 +68,7 @@ class Rop {
public function __construct() {

$this->plugin_name = 'rop';
$this->version = '9.0.22';
$this->version = '9.0.23';

$this->load_dependencies();
$this->set_locale();
Expand Down
2 changes: 1 addition & 1 deletion package.json
@@ -1,6 +1,6 @@
{
"name": "tweet-old-post",
"version": "9.0.22",
"version": "9.0.23",
"description": "Tweet Old Posts plugin for WordPress.",
"repository": {
"type": "git",
Expand Down
125 changes: 83 additions & 42 deletions readme.txt

Large diffs are not rendered by default.

5 changes: 3 additions & 2 deletions tweet-old-post.php
Expand Up @@ -16,7 +16,7 @@
* Plugin Name: Revive Old Posts
* Plugin URI: https://revive.social/
* 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!
* Version: 9.0.22
* Version: 9.0.23
* Author: revive.social
* Author URI: https://revive.social/
* Requires at least: 4.7
Expand Down Expand Up @@ -162,7 +162,7 @@ function run_rop() {
define( 'ROP_CRON_ALTERNATIVE', $use_remote_cron );

define( 'ROP_PRO_URL', 'http://revive.social/plugins/revive-old-post/' );
define( 'ROP_LITE_VERSION', '9.0.22' );
define( 'ROP_LITE_VERSION', '9.0.23' );
define( 'ROP_LITE_BASE_FILE', __FILE__ );
$debug = false;
if ( function_exists( 'wp_get_environment_type' ) ) {
Expand All @@ -188,6 +188,7 @@ function run_rop() {
define( 'ROP_APP_VK_PATH', '/vk_auth' );
define( 'ROP_INSTALL_TOKEN_OPTION', 'rop_install_token' );
define( 'ROP_POST_SHARING_CONTROL_API', ROP_AUTH_APP_URL . '/wp-json/auth-option/v1/post-sharing-control' );
define( 'ROP_POST_ON_X_API', ROP_AUTH_APP_URL . '/wp-json/auth-option/v1/post-on-x' );

$vendor_file = ROP_LITE_PATH . '/vendor/autoload.php';
if ( is_readable( $vendor_file ) ) {
Expand Down
64 changes: 58 additions & 6 deletions vue/src/vue-elements/logs-tab-panel.vue
Expand Up @@ -7,7 +7,16 @@
>
<div class="column col-12 text-right ">
<button
class="btn btn-secondary "
class="btn btn-secondary "
@click="exportLogsAsFile"
>
<i
class="fa fa-download"
/>
{{ labels.export_btn }}
</button>
<button
class="btn btn-secondary "
@click="getLogs(true)"
>
<i
Expand Down Expand Up @@ -50,11 +59,13 @@
class="column col-12 mt-2"
>
<div
class="toast log-toast"
:class="'toast-' + data.type"
class="log-container"
>
<small class="pull-right text-right">{{ formatDate ( data.time ) }}</small>
<p>{{ data.message }}</p>
[<span>{{ formatDate ( data.time ) }}</span>]
[<span
:class="'log-' + data.type"
>{{ data.type }}</span>]
{{ data.message }}
</div>
</div>
</template>
Expand Down Expand Up @@ -129,14 +140,29 @@
}
return moment.utc(value, 'X').format(format.replace('mm', 'mm:ss'));
},
exportLogsAsFile() {
const content = this.logs.map(log => {
return `[${moment.utc(log.time, 'X')}][${log.type}] ${log.message}`;
}).join('\n');
const element = document.createElement('a');
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(content));
element.setAttribute('download', `rop_logs__${moment().format('YYYY-MM-DD_HH-mm-ss')}.txt`);
element.style.display = 'none';
document.body.appendChild(element);
element.click();
document.body.removeChild(element);
}
},
}
</script>
<style type="text/css" scoped>
<style lang="scss" scoped>
#rop_core .toast.log-toast p {
margin: 0px;
line-height: inherit;
padding: 20px 5px;
}
#rop_core .toast.log-toast:hover {
Expand All @@ -156,4 +182,30 @@
.columns {
line-break: anywhere;
}
.log-container {
font-size: 14px;
background-color: #f3f2f1;
padding: 10px;
span {
text-transform: uppercase;
&:nth-child(even) {
font-weight: bold;
}
&.log-error {
color: #BE4B00;
}
&.log-success {
color: #418331;
}
}
&:has( .log-error ) {
background-color: #FBE8E8;
}
}
</style>
39 changes: 30 additions & 9 deletions vue/src/vue-elements/post-format.vue
Expand Up @@ -522,6 +522,12 @@
<div class="column col-6 col-sm-12 vertical-align rop-control">
<b>{{ labels.image_title }}</b>
<p class="text-gray">
<span
v-if="is_twitter && is_sharing_post_via_rop_server"
class="block"
>
{{ labels.not_available_with_rop_server }}
</span>
<span v-html="labels.image_desc" />
</p>
</div>
Expand All @@ -532,7 +538,7 @@
v-if="!is_instagram_account"
v-model="post_format.image"
type="checkbox"
:disabled="!isPro"
:disabled="!isPro || (is_twitter && is_sharing_post_via_rop_server)"
>
<!-- For instagram accounts -->
<input
Expand Down Expand Up @@ -695,24 +701,35 @@
const services = this.$store.state.authenticatedServices;
for (const key in services) {
if (!services.hasOwnProperty(key)) {
for (const key in services) {
if (!services.hasOwnProperty(key)) {
continue;
}
const service = services[key];
for (const account_id in service.available_accounts) {
if (!service.available_accounts.hasOwnProperty(account_id)) {
continue;
}
const service = services[key];
all_accounts[account_id] = service.available_accounts[account_id];
for (const account_id in service.available_accounts) {
if (!service.available_accounts.hasOwnProperty(account_id)) {
continue;
}
all_accounts[account_id] = service.available_accounts[account_id];
if ( service?.credentials?.rop_auth_token ) {
all_accounts[account_id].is_using_rop_server = true;
}
}
}
return all_accounts;
},
is_instagram_account: function(){
return this.allAccounts[this.account_id].account_type === 'instagram_account';
},
is_twitter: function(){
return this.allAccounts[this.account_id].service === 'twitter';
},
is_sharing_post_via_rop_server: function(){
return this.allAccounts[this.account_id]?.is_using_rop_server;
},
postTypes: function () {
return this.$store.state.generalSettings.available_post_types;
},
Expand Down Expand Up @@ -850,4 +867,8 @@
text-align: left;
}
}
.block {
display: block;
}
</style>
12 changes: 11 additions & 1 deletion vue/src/vue-elements/pro/publish-now.vue
Expand Up @@ -28,7 +28,7 @@
>
<input
type="checkbox"
:checked="share_on_update_by_default && !choose_accounts_manually"
:checked="isActiveAccount(key)"
:value="key"
name="publish_now_accounts[]"
class="rop-account-names"
Expand Down Expand Up @@ -91,6 +91,7 @@
choose_accounts_manually: this.$store.state.publish_now.choose_accounts_manually,
showField: fields,
toggle_accounts: this.$store.state.publish_now.instant_share_by_default,
active_accounts: this.$store.state.publish_now.active
}
},
computed: {
Expand Down Expand Up @@ -130,6 +131,15 @@
return self.showField[value] = ! self.showField[value];
},
containsKey(obj, key ) {
return Object.keys(obj).includes(key);
},
isActiveAccount: function( key ) {
var self = this;
return this.containsKey(self.active_accounts, key);
},
}
}
</script>
Expand Down
244 changes: 130 additions & 114 deletions vue/src/vue-elements/sign-in-btn.vue

Large diffs are not rendered by default.