Plugin Directory

Changeset 3258349


Ignore:
Timestamp:
03/19/2025 09:23:26 AM (11 months ago)
Author:
shorthandconnect
Message:

Created tag 1.3.32

Location:
shorthand-connect
Files:
87 added
17 deleted
8 edited
12 copied

Legend:

Unmodified
Added
Removed
  • shorthand-connect/tags/1.3.32/includes/api.php

    r3095659 r3258349  
    11<?php
     2/**
     3 * Shorthand API functions.
     4 *
     5 * @package Shorthand Connect
     6 */
    27
    38/*
     
    1116 * @param array $options An array of extra options args to pass to wp_remote_request
    1217 */
    13 function sh_v2_api_request( $url, $options ) {
     18function shorthand_api_v2_request( $url, $options ) {
    1419    $token = get_option( 'sh_v2_token' );
    1520    if ( ! $token ) {
     
    4449}
    4550
    46 function sh_v2_api_request_json( $url, $options ) {
    47     $response = sh_v2_api_request( $url, $options );
     51/**
     52 * Makes a JSON API request to the shorthand API.
     53 *
     54 * @param string $url The URL of the API endpoint.
     55 * @param array  $options Optional arguments for the API request.
     56 * @return mixed The decoded JSON response, or null if an error occurs.
     57 */
     58function shorthand_api_request_json( $url, $options ) {
     59    $response = shorthand_api_v2_request( $url, $options );
    4860    $body     = wp_remote_retrieve_body( $response );
    4961    return json_decode( $body );
    5062}
    5163
    52 function sh_get_profile() {
     64/**
     65 * Retrieves user profile information from the shorthand API.
     66 *
     67 * @return object User profile information, including username, gravatar, and other potential data.
     68 */
     69function shorthand_api_get_profile() {
    5370    $tokeninfo = array();
    5471
    55     $data = sh_v2_api_request_json( '/v2/token-info', array() );
     72    $data = shorthand_api_request_json( '/v2/token-info', array() );
    5673    if ( $data && isset( $data->organisation_id ) ) {
    5774        $tokeninfo['username'] = $data->name . ' (' . $data->token_type . ' Token)';
     
    6380}
    6481
    65 function sh_get_stories() {
     82/**
     83 * Retrieves stories from the shorthand API.
     84 *
     85 * Fetches a list of stories from the shorthand API, optionally filtered by keyword.
     86 *
     87 * @param string $keyword Optional keyword to filter stories by.
     88 * @return array|null An array of story data, or null if an error occurs or no stories are found.
     89 */
     90function shorthand_api_get_stories(  string $keyword = '', string $cursor = '', string $limit = '50' ) {
    6691    $stories = null;
    67 
    68     $data = sh_v2_api_request_json( '/v2/stories', array( 'timeout' => '240' ) );
     92    $url     = '/v2/stories';
     93    $url     .= '?limit='.$limit;
     94    if ( $cursor && $cursor != '') {
     95        $url .= '&cursor=' . $cursor;
     96    }
     97    if ( $keyword && $keyword != '' ) {
     98        $url .= '&keyword=' . $keyword;
     99    }
     100   
     101
     102    $data = shorthand_api_request_json( $url, array( 'timeout' => '240' ) );
    69103    if ( $data ) {
    70104        $stories = array();
    71         // Something went wrong
     105        // Something went wrong.
    72106        if ( isset( $data->status ) && $data->status ) {
    73107            return null;
    74108        }
     109
    75110        foreach ( $data as $storydata ) {
    76             $story     = array(
    77                 'image'         => $storydata->signedCover,
    78                 'id'            => $storydata->id,
    79                 'metadata'      => (object) array(
    80                     'description' => $storydata->description,
    81                 ),
    82                 'title'         => $storydata->title,
    83                 'story_version' => '' . $storydata->version,
     111            $updated_timestamp   = strtotime( esc_html( $storydata->updatedAt ) );
     112            $updated_at          = $storydata->updatedAt;
     113            $published_timestamp = strtotime( esc_html( $storydata->lastPublishedAt ) );
     114            $updated             = human_time_diff( $updated_timestamp, current_time( 'timestamp' ) );
     115            $published           = human_time_diff( $published_timestamp, current_time( 'timestamp' ) );
     116            $stories[]           = array(
     117                'version_value'       => '' . $storydata->version,
     118                'title'               => $storydata->title,
     119                'description'         => $storydata->description,
     120                'imagealt'            => $storydata->title,
     121                'image'               => $storydata->signedCover,
     122                'updated_timestamp'   => $updated_timestamp,
     123                'updated_at'          => $updated_at,
     124                'updated_value'       => $updated,
     125                'published_timestamp' => $published_timestamp,
     126                'published_value'     => $published,
     127                'story_id'            => $storydata->id,
     128                'class'               => 'story',
    84129            );
    85             $stories[] = (object) $story;
    86130        }
    87131    }
     
    90134}
    91135
     136/**
     137 * Gets the path to the story directory.
     138 *
     139 * Determines the path to the directory where story files for a specific post
     140 * and story ID should be stored.
     141 *
     142 * @param int    $post_id The ID of the post.
     143 * @param string $story_id The ID of the story.
     144 * @return string|null The path to the story directory, or null if the directory doesn't exist or an error occurs.
     145 */
    92146function sh_get_story_path( $post_id, $story_id ) {
    93147    init_wp_filesystem();
     
    95149    $destination_path = $destination['path'] . '/shorthand/' . $post_id . '/' . $story_id;
    96150
    97     // on WP VIP, folders in the uploads dir always exist
     151    // On WP VIP, folders in the uploads dir always exist.
    98152    if ( ! file_exists( $destination_path ) ) {
    99153        $destination_path = null;
     
    105159}
    106160
     161/**
     162 * Gets the URL of the story directory.
     163 *
     164 * Determines the URL of the directory where story files for a specific post
     165 * and story ID are located.
     166 *
     167 * @param int    $post_id The ID of the post.
     168 * @param string $story_id The ID of the story.
     169 * @return string The URL of the story directory.
     170 */
    107171function shorthand_get_story_url( $post_id, $story_id ) {
    108172    init_wp_filesystem();
     
    123187    $story            = array();
    124188
    125     // Attempt to connect to the server
    126     $zip_file          = wp_tempnam( 'sh_zip', $tmpdir );
    127     $response          = sh_v2_api_request(
     189    // Attempt to connect to the server.
     190    $zip_file = wp_tempnam( 'sh_zip', $tmpdir );
     191    $response = shorthand_api_v2_request(
    128192        '/v2/stories/' . $story_id . ( $without_assets ? '?without_assets=true' : '' ) . ( $assets_only ? '?assets_only=true' : '' ),
    129193        array(
     
    145209        );
    146210    } else {
    147         $story = extractStoryContent( $zip_file, $destination_path, $story_id );
     211        $story = extract_story_content( $zip_file, $destination_path, $story_id );
    148212    }
    149213
     
    153217}
    154218
    155 
     219/**
     220 * Initializes the WordPress filesystem.
     221 *
     222 * Ensures that the WordPress filesystem is available for file operations.
     223 * If not, it requests filesystem credentials and re-initializes the filesystem.
     224 */
    156225function init_wp_filesystem() {
    157226    WP_Filesystem();
     
    163232}
    164233
    165 function extractStoryContent( $zip_file, $destination_path, $story_id ) {
     234/**
     235 * Extracts story content from a ZIP file.
     236 *
     237 * Extracts the 'head.html' and 'article.html' files from a given ZIP file
     238 * into a specified destination path.
     239 *
     240 * @param string $zip_file The path to the ZIP file.
     241 * @param string $destination_path The path to the extraction destination.
     242 * @param string $story_id The ID of the story.
     243 * @return array An array containing extracted content, path, or error information.
     244 */
     245function extract_story_content( $zip_file, $destination_path, $story_id ) {
    166246    // WP VIP HOSTING COMPATIBILITY
    167247    $zip = new ZipArchive();
  • shorthand-connect/tags/1.3.32/includes/mass-pull.php

    r3095659 r3258349  
    11<?php
    2 /*
    3  * Sets each custom meta field, should be paired with sh_copy_story().
     2/**
     3 * Bulk story pull functions.
    44 *
     5 * @package Shorthand Connect
     6 */
     7
     8/**
     9 * Updates a story for a given post.
     10 *
     11 * Fetches story data from the shorthand API, copies story files, updates post
     12 * metadata, and performs post-processing on the story content.
    513 * Note: Unzipping a fresh story copy won't update the fields.
     14 *
     15 * @param int    $post_id The ID of the post.
     16 * @param string $story_id The ID of the story.
    617 */
    718function shand_update_story( $post_id, $story_id ) {
     
    1425
    1526    // Grab JSON for Story & Set into object $obj.
    16     $obj = sh_v2_api_request_json( '/v2/stories/' . $story_id . '/settings', array( 'timeout' => '240' ) );
     27    $obj = shorthand_api_request_json( '/v2/stories/' . $story_id . '/settings', array( 'timeout' => '240' ) );
    1728
    1829    $err = sh_copy_story( $post_id, $safe_story_id, $sh_media_cron_offload );
  • shorthand-connect/tags/1.3.32/includes/shorthand-options.php

    r3095659 r3258349  
    11<?php
     2/**
     3 * Admin settings for the first time dialog.
     4 *
     5 * @package Shorthand Connect
     6 */
     7
    28/* Options */
    3 function shand_shorthand_menu() {
    4     add_options_page( 'Shorthand Options', 'Shorthand', 'manage_options', 'shorthand-options', 'shand_shorthand_options' );
    5 }
    6 $default_sh_site_css = '
    7 /* START CSS FOR DEFAULT WP THEMES */
    8 .site {
    9     margin: 0;
    10     max-width: none;
    11 }
    12 .site-content {
    13     padding: 0 !important;
    14 }
    15 .site-inner {
    16     max-width: none;
    17 }
    18 .site-header {
    19     max-width: none;
    20     z-index: 100;
    21 }
    22 .site:before {
    23     width: 0;
    24 }
    25 /* END CSS FOR DEFAULT WP THEMES */
    26 ';
    27 
    28 // JSON Checker
    29 function validate_json( $json_string ) {
    30     // Try to decode the JSON data. If it fails, the JSON is invalid.
    31     $json_data = json_decode( $json_string, true );
    32 
    33     if ( json_last_error() !== JSON_ERROR_NONE ) {
    34         // The JSON is invalid.
    35         return false;
    36     }
    37 
    38     // Return the original JSON string if it's valid.
    39     return $json_string;
    40 }
    41 
    42 function shand_shorthand_options() {
    43     global $default_sh_site_css;
    44     global $server_url;
    45 
    46     // Rather than running a rewrite flush everytime a post is submitted, run it on plugin activate/deactivate
    47     function shand_rewrite_flush() {
    48         shand_create_post_type();
    49         flush_rewrite_rules();
    50     }
    51     register_activation_hook( __FILE__, 'shand_rewrite_flush' );
    52     register_deactivation_hook( __FILE__, 'flush_rewrite_rules' );
     9
     10// Redirect to the first time setup.
     11function shorthand_redirect_admin_config() {
     12    $profile = shorthand_api_get_profile();
     13    if ( isset( $_GET['page'] ) && ( 'shorthand-options' === $_GET['page'] ) && ( ! $profile ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
     14        $admin_url = Shorthand_Admin::get_page_url( 'init' );
     15        wp_redirect( $admin_url );
     16        die();
     17    }
     18}
     19add_action( 'admin_menu', 'shorthand_redirect_admin_config' );
     20
     21function shorthand_shorthand_options() {
     22    // Menu links.
     23    $menu_links = array(
     24        'token'        => 'API Key',
     25        'permalink'    => 'Permalinks',
     26        'css'          => 'Custom CSS',
     27        'processing'   => 'Post-processing',
     28        'experimental' => 'Experimental Features',
     29    );
     30
     31    // If current = all, then display all.
     32    $current  = isset( $_GET['navigation'] ) ? sanitize_key( $_GET['navigation'] ) : array_keys( $menu_links )[0];
     33    $profile  = shorthand_api_get_profile();
     34    $messages = array();
    5335
    5436    if ( ! current_user_can( 'manage_options' ) ) {
    5537        wp_die( esc_html( __( 'You do not have sufficient permissions to access this page.' ) ) );
    5638    }
     39
    5740    if ( isset( $_POST['sh_submit_hidden'] ) && 'Y' === $_POST['sh_submit_hidden'] && check_admin_referer( 'sh-update-configuration' ) ) {
    5841        // If there's a token set, use it, if not set it to an empty string
    5942        $sh_v2_token = isset( $_POST['sh_v2_token'] ) ? sanitize_text_field( $_POST['sh_v2_token'] ) : '';
    6043        update_option( 'sh_v2_token', $sh_v2_token );
     44
     45        $profile = shorthand_api_get_profile();
     46        if ( $profile ) {
     47            $messages['updated'] = '<p>' . SHORTHAND_CONFIG_STEP1_SUCCESS . '</p><p><strong>Username</strong>: ' . esc_html( $profile->username ) . '</p>';
     48        } else {
     49            $messages['notice-error'] = SHORTHAND_CONFIG_STEP1_ERROR;
     50        }
    6151    }
    6252
     
    6454
    6555    if ( isset( $_POST['sh_submit_hidden_two'] ) && 'Y' === $_POST['sh_submit_hidden_two'] && check_admin_referer( 'sh-update-configuration' ) ) {
    66         // Check if there's custom CSS, if there is, use wp_kses_post() to sanitize otherwise set an empty string
     56        // Check if there's custom CSS, if there is, use wp_kses_post()
     57        // to sanitize otherwise set an empty string.
    6758        $sh_css = isset( $_POST['sh_css'] ) ? wp_kses_post( $_POST['sh_css'] ) : '';
    6859        update_option( 'sh_css', $sh_css );
     60        $messages['updated'] = SHORTHAND_CONFIG_STEP3_SUCCESS;
    6961    }
    7062
    7163    if ( isset( $_POST['sh_submit_hidden_three'] ) && 'Y' === $_POST['sh_submit_hidden_three'] && check_admin_referer( 'sh-update-configuration' ) ) {
    72         // Check if there's custom permalink, if there is, use sanitize_text_field() to sanitize potential HTML and then set an empty string
     64        // Check if there's custom permalink, if there is, use sanitize_text_field()
     65        // to sanitize potential HTML and then set an empty string.
    7366        $sh_permalink = isset( $_POST['sh_permalink'] ) ? sanitize_text_field( $_POST['sh_permalink'] ) : '';
    7467        update_option( 'sh_permalink', $sh_permalink );
    75         shand_rewrite_flush();
     68        shorthand_rewrite_flush();
     69        $messages['updated'] = SHORTHAND_CONFIG_STEP2_SUCCESS;
    7670    }
    7771    $permalink_structure = esc_html( get_option( 'sh_permalink' ) );
     
    8276    }
    8377    $sh_css = get_option( 'sh_css' );
    84     $no_css = false;
    85     if ( '' === $sh_css ) {
    86         $no_css = true;
    87         if ( isset( $default_site_css ) ) {
    88             update_option( 'sh_css', $default_site_css );
    89         }
    90         $sh_css = $default_sh_site_css;
    91     }
    92 
    93     if ( isset( $_POST['sh_submit_hidden_four'] ) && 'Y' === $_POST['sh_submit_hidden_four'] && check_admin_referer( 'sh-update-configuration' ) ) {
    94         $sh_regex_list = isset( $_POST['sh_regex_list'] ) ? wp_unslash( $_POST['sh_regex_list'] ) : '';
    95 
     78
     79    if ( isset( $_POST['sh_submit_hidden_four'] ) &&
     80        ( 'Y' === $_POST['sh_submit_hidden_four'] ) &&
     81        check_admin_referer( 'sh-update-configuration' )
     82    ) {
     83        // sh_regex_list may contain <tags> for lookup and processing on import and so may need to include <script> etc; however it is only ever displayed within a text-area value and manually processed.
     84        $sh_regex_list = empty( $_POST['sh_regex_list'] ) ?  '' : wp_unslash( $_POST['sh_regex_list'] ); //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
     85
     86        $messages['updated'] = SHORTHAND_CONFIG_STEP4_SUCCESS;
    9687        if ( empty( $sh_regex_list ) ) {
    97             // Update the option with an empty value if the input is empty
     88            // Update the option with an empty value if the input is empty.
    9889            update_option( 'sh_regex_list', '' );
    9990        } else {
    10091            // Validate if it's a valid JSON without sanitizing
    101             $sh_regex_list = validate_json( $sh_regex_list );
     92            $sh_regex_list = Shorthand_Admin::validate_json( $sh_regex_list );
    10293
    10394            if ( false !== $sh_regex_list ) {
     
    10697            } else {
    10798                // Handle invalid JSON error here.
     99                unset( $messages['updated'] );
     100                $messages['notice-error'] = SHORTHAND_CONFIG_STEP4_ERROR;
    108101            }
    109102        }
     
    112105    $sh_regex_list = base64_decode( get_option( 'sh_regex_list' ) );
    113106
    114     // Experimental Settings
     107    // Experimental settings.
    115108    if ( isset( $_POST['sh_submit_hidden_experimental'] ) && 'Y' === $_POST['sh_submit_hidden_experimental'] && check_admin_referer( 'sh-update-configuration' ) ) {
    116109        $sh_media_cron_offload = isset( $_POST['sh_media_cron_offload'] ) ? filter_var( $_POST['sh_media_cron_offload'], FILTER_VALIDATE_BOOLEAN ) : false;
     
    118111        update_option( 'sh_media_cron_offload', $sh_media_cron_offload );
    119112        update_option( 'sh_disable_acf', $sh_disable_acf );
     113        $messages['updated'] = SHORTHAND_CONFIG_STEP5_SUCCESS;
    120114    }
    121115    $sh_media_cron_offload = filter_var( get_option( 'sh_media_cron_offload' ), FILTER_VALIDATE_BOOLEAN );
    122116    $sh_disable_acf        = filter_var( get_option( 'sh_disable_acf' ), FILTER_VALIDATE_BOOLEAN );
    123117
    124     $profile = sh_get_profile();
    125     ?>   
    126 <div class="container">
    127     <div class="py-1">
    128     <h1>Shorthand API Configuration</h1>
    129     <h2>Shorthand Connect Status</h2>
    130     <form name="formtoken" method="post">
    131     <?php wp_nonce_field( 'sh-update-configuration' ); ?>
    132         <input type="hidden" name="sh_submit_hidden" value="Y" />
    133         <table class="form-table"><tbody>
    134         <tr class="v2row">
    135         <th scope="row"><label for="sh_v2_token"><?php esc_html_e( 'Shorthand Team Token', 'sh-v2-token' ); ?></label></th>
    136         <td><input type="text" id="sh_v2_token" name="sh_v2_token" value="<?php echo esc_attr( $v2_token ); ?>" size="28"></td>
    137         </tr>
    138         </tbody></table>
    139     <?php if ( $profile ) { ?>
    140         <p class="status">Successfully connected</p>
    141         <p><strong>Username</strong>: <?php echo esc_html( $profile->username ); ?></p>
    142     <?php } else { ?>
    143         <p class="status warn">Not Connected</p>
    144     <?php } ?>
    145     <div style='clear:both'></div>
    146         <p class="submit">
    147         <input type="submit" name="Submit" class="button-primary" value="<?php esc_attr_e( 'Save Changes' ); ?>" />
     118    ?>
     119    <div class="wrap shorthand-options-wrapper">
     120    <h1>Settings</h1>
     121
     122    <hr />
     123    <div class="shorthand-menu">
     124        <ol role="list">
     125        <?php foreach ( $menu_links as $menu_key => $menu_name ) { ?>
     126        <li class="py-1">
     127        <form name="menu_settings_<?php echo esc_attr( $menu_key ); ?>" method="post" action="<?php echo esc_url( Shorthand_Admin::get_page_url( 'config', esc_attr( $menu_key ) ) ); ?>">
     128            <input type="submit" name="Submit" class="button<?php echo ( in_array( $current, array( $menu_key, 'all' ) ) ) ? ' button button-primary' : ''; ?>" value="<?php esc_attr_e( $menu_name ); ?>" />
     129        </form>
     130        </li>
     131        <?php } ?>
     132        </ol>
     133    </div>
     134
     135    <div class="shorthand-form-wrapper">
     136    <form name="form_settings" method="post" action="<?php echo esc_url( Shorthand_Admin::get_page_url( 'config', esc_attr( $current ) ) ); ?>">
     137
     138        <?php if ( ! empty( $messages ) ) : ?>
     139            <?php foreach ( $messages as $class => $message ) : ?>
     140            <div class="notice <?php echo esc_html ( $class ); ?>"><?php echo wp_kses( $message, array(
     141    'p' => array(),
     142    'strong' => array(),
     143) ); ?></div>
     144            <?php endforeach; ?>
     145        <?php endif; ?>
     146
     147        <?php if ( in_array( $current, array( array_keys( $menu_links )[0], 'all' ) ) ) : ?>
     148        <div class="py-1">
     149            <h2>Shorthand API Key</h2>
     150            <?php wp_nonce_field( 'sh-update-configuration' ); ?>
     151        <input type="hidden" name="sh_submit_hidden" value="Y"/>
     152        <p><label for="sh_v2_token"><?php esc_html_e( 'Your API key provided by Shorthand', 'shorthand-connect' ); ?></label></p>
     153        <input type="text" id="sh_v2_token" name="sh_v2_token" value="<?php echo esc_attr( $v2_token ); ?>">
     154        <p>
     155            <?php esc_html_e( 'Do not have API key?', 'shorthand-connect' ); ?> <a target="_blank" alt="(<?php esc_html_e( 'opens Shorthand Connect plugin settings', 'shorthand-connect' ); ?>)" href="https://support.shorthand.com/en/articles/62-programmatic-publishing-with-the-shorthand-api"><?php esc_html_e( 'Get one here', 'shorthand-connect' ); ?></a>
    148156        </p>
    149     </form>
    150     </div>
    151 
    152 
    153     <div class="py-1">
    154     <h2>Shorthand Permalink Structure</h2>
    155         <p>Use this to set the permalink structure of Shorthand story URLs</p>
    156         <form name="permalinks" method="post">
    157     <?php wp_nonce_field( 'sh-update-configuration' ); ?>
    158             <input type="hidden" name="sh_submit_hidden_three" value="Y" />
    159             <p>
    160                 <?php _e( 'Permalink structure:', 'sh-permalink-value' ); ?><br /><?php echo esc_url( get_site_url() ); ?>/<input type="text" name="sh_permalink" value="<?php echo esc_attr( $permalink_structure ); ?>" size="20">/{STORY_NAME}
    161             </p>
    162             <p class="submit">
    163                 <input type="submit" name="Submit" class="button-primary" value="<?php esc_attr_e( 'Save Changes' ); ?>" />
    164             </p>
    165         </form>
    166     </div>
    167 
    168     <div class="py-1">
    169     <h2>Shorthand Story Page CSS (theme wide CSS)</h2>
    170         <p>Use this CSS to customise Shorthand Story pages to better suit your theme</p>
    171     <?php if ( $no_css ) { ?>
    172             <p class="status warn">No custom CSS found, using default theme CSS</p>
    173     <?php } ?>
    174         <form name="themecss" method="post">
    175     <?php wp_nonce_field( 'sh-update-configuration' ); ?>
    176             <input type="hidden" name="sh_submit_hidden_two" value="Y" />
    177             <textarea rows="10" cols="80" name="sh_css"><?php echo esc_textarea( $sh_css ); ?></textarea>
    178             <p class="submit">
    179                 <input type="submit" name="Submit" class="button-primary" value="<?php esc_attr_e( 'Save Changes' ); ?>" />
    180             </p>
    181         </form>
    182         </div>
    183 
    184     <div class="py-1">   
    185     <h2>Post-processing</h2>
    186         <p>Use this to create a JSON object of regex queries and replacements.</p>
     157            <div style='clear:both'></div>
     158        </div>
     159        <?php endif; ?>
     160
     161        <?php if ( in_array( $current, array( array_keys( $menu_links )[1], 'all' ) ) ) : ?>
     162        <div class="py-1">
     163        <h2>Permalink Structure</h2>
     164            <?php wp_nonce_field( 'sh-update-configuration' ); ?>
     165        <input type="hidden" name="sh_submit_hidden_three" value="Y"/>
     166        <p>
     167        <p><label for="sh_permalink"><?php esc_html_e( 'Set the permalink structure of Shorthand story URLs', 'shorthand-connect' ); ?></label></p>
     168        <input type="text" name="sh_permalink" value="<?php echo esc_attr( $permalink_structure ); ?>" size="20">
     169        <p><?php echo esc_url( get_site_url() ); ?>/<strong><?php echo esc_html( $permalink_structure ); ?></strong>/{STORY_NAME}</p>
     170        </div>
     171        <?php endif; ?>
     172
     173        <?php if ( in_array( $current, array( array_keys( $menu_links )[2], 'all' ) ) ) : ?>
     174        <div class="py-1">
     175        <h2>Custom CSS</h2>
     176        <p>Use theme wide CSS to customise Shorthand Story pages to better suit your theme</p>
     177            <?php wp_nonce_field( 'sh-update-configuration' ); ?>
     178        <input type="hidden" name="sh_submit_hidden_two" value="Y"/>
     179        <textarea rows="10" cols="80" name="sh_css"><?php echo esc_textarea( $sh_css ); ?></textarea>
     180        </div>
     181        <?php endif; ?>
     182
     183        <?php if ( in_array( $current, array( array_keys( $menu_links )[3], 'all' ) ) ) : ?>
     184        <div class="py-1">
     185        <h2>Post-processing</h2>
     186        <p>Create a JSON object of regex queries and replacements.</p>
    187187        <p><em>This Example removes title tags from within the head tag by replacing it with nothing.</em></p>
    188 <pre><code>
     188        <pre><code class="post-processing">
    189189{
    190190    "head": [
    191191    {
    192         &quot;query&quot;: &quot;/&lt;title&gt;(.*?)&lt;\\/title&gt;/&quot;,
    193         &quot;replace&quot;: &quot;&quot;
     192    &quot;query&quot;: &quot;/&lt;title&gt;(.*?)&lt;\\/title&gt;/&quot;,
     193    &quot;replace&quot;: &quot;&quot;
    194194    }
    195195    ],
    196196    "body": []
    197197}
    198 
    199 </code></pre>
    200         <form name="postprocessing" method="post">
    201     <?php wp_nonce_field( 'sh-update-configuration' ); ?>
    202             <input type="hidden" name="sh_submit_hidden_four" value="Y" />
    203             <textarea rows="10" cols="80" id="sh_regex_list" name="sh_regex_list"><?php echo esc_textarea( $sh_regex_list ); ?></textarea>
    204             <p class="submit">
    205                 <input type="submit" name="Submit" class="button-primary" value="<?php esc_attr_e( 'Save Changes' ); ?>" />
    206             </p>
    207         </form>
     198        </code></pre>
     199            <?php wp_nonce_field( 'sh-update-configuration' ); ?>
     200        <input type="hidden" name="sh_submit_hidden_four" value="Y"/>
     201        <textarea rows="10" cols="80" id="sh_regex_list"
     202        name="sh_regex_list"><?php echo esc_textarea( $sh_regex_list ); ?></textarea>
    208203        <script>
    209204            let textarea = document.querySelector("textarea#sh_regex_list");
    210        
    211             textarea.addEventListener("keyup", function(event) {
    212                 try {
    213                     JSON.parse(textarea.value);
     205           
     206            textarea.addEventListener("keyup", function (event) {
     207            try {
     208            textarea.value = JSON.stringify(JSON.parse(textarea.value), undefined, 4);
     209            textarea.setCustomValidity("");
     210
     211            } catch (err) {
     212                if (textarea.value != "") {
     213                    console.log("Invalid JSON");
     214                    textarea.setCustomValidity("Invalid JSON in the Post-processing field");
     215                } else {
    214216                    textarea.setCustomValidity("");
    215                    
    216                 } catch(err) {
    217                     if(textarea.value != ""){
    218                         console.log("Invalid JSON");
    219                         textarea.setCustomValidity("Invalid JSON in the Post-processing field");
    220                     } else {
    221                         textarea.setCustomValidity("");
    222                     }
    223217                }
    224                
     218            }
     219
    225220            });
    226221        </script>
    227     </div>
    228 
    229     <div class="py-1">
    230     <h2>Experimental Features</h2>
    231     <p>Early access features that are still subject to change.</p>
    232     <form name="form_experimental" method="post">
    233     <?php wp_nonce_field( 'sh-update-configuration' ); ?>
     222        </div>
     223        <?php endif; ?>
     224
     225        <?php if ( in_array( $current, array( array_keys( $menu_links )[4], 'all' ) ) ) : ?>
     226        <div class="py-1">
     227        <h2>Experimental Features <span class="badge badge-blue">Advanced</span></h2>
     228        <p>Early access features that are still subject to change.</p>
     229            <?php wp_nonce_field( 'sh-update-configuration' ); ?>
    234230        <input type="hidden" name="sh_submit_hidden_experimental" value="Y"/>
    235         <input type="checkbox" id="sh_media_cron_offload" name="sh_media_cron_offload"
    236             value="true" <?php echo esc_attr( $sh_media_cron_offload ? 'checked' : '' ); ?> />
    237         <label for="sh_media_cron_offload">Import media assets via cron</label>
    238         <p>Assets will be fetched after story save to prevent potential execution timeouts. Media won't be immediately
    239         available on save but progress will be updated based on the `media_status` field.</p>
    240         <p>It is advised that Shorthand Story Posts are saved as a draft first to trigger the cron job prior to public
    241         publishing.</p>
     231
     232        <div class="checkbox-container">
     233        <input type="checkbox" id="sh_media_cron_offload" name="sh_media_cron_offload" <?php echo esc_attr( $sh_media_cron_offload ? 'checked' : '' ); ?> />
     234        <div class="bordered">
     235            <label for="sh_media_cron_offload"><strong>Import media assets via cron</strong>
     236            <p>Assets will be fetched after story save to prevent potential execution timeouts. Media won't be immediately
     237            available on save but progress will be updated based on the `media_status` field.</p>
     238            <p>It is advised that Shorthand Story Posts are saved as a draft first to trigger the cron job prior to public
     239            publishing.</p>
     240            </label>
     241        </div>
     242        </div>
     243
    242244        <br/>
     245        <div class="checkbox-container">
    243246        <input type="checkbox" id="sh_disable_acf" name="sh_disable_acf"
    244             value="true" <?php echo esc_attr( $sh_disable_acf ? 'checked' : '' ); ?> />
    245         <label for="sh_disable_acf">Disable Advanced Custom Fields</label>
    246         <p>Used to prevent any potential issues that could cause the Shorthand Custom Fields to become hidden by Advanced
    247         Custom Fields.</p>
     247        value="true" <?php echo esc_attr( $sh_disable_acf ? 'checked' : '' ); ?> />
     248        <div class="bordered"><label for="sh_disable_acf"><strong>Disable Advanced Custom Fields</strong>
     249            <p>Used to prevent any potential issues that could cause the Shorthand Custom Fields to become hidden by Advanced Custom Fields.</p></label></div>
     250        </div>
    248251        </br>
     252        </div>
     253        <?php endif; ?>
     254
     255        <div class="py-1">
     256        <hr />
    249257        <p class="submit">
    250         <input type="submit" name="Submit" class="button-primary" value="<?php esc_attr_e( 'Save Changes' ); ?>"/>
     258            <input type="submit" name="Submit" class="button button-primary" value="<?php esc_attr_e( 'Save changes' ); ?>"/>
    251259        </p>
     260        </div>
    252261    </form>
    253     </div>
    254 </div>
    255     </div>
    256 <style>
    257         .py-1 {
    258             padding: 1em;
    259         }
    260         .bg-white {
    261             background: white;
    262         }
    263         .container {
    264             max-width: 980px;
    265         }
    266         img.grav {
    267             float: left;
    268             width:80px;
    269             margin-right:10px;
    270         }
    271         p.status {
    272             background:#dfd;
    273             color:green;
    274             font-weight:bold;
    275             width:350px;
    276             clear:left;
    277             padding:5px;
    278         }
    279         p.status.warn {
    280             background:#ffd;
    281             color:orange;
    282         }
    283         .row-hidden {
    284             display:none;
    285         }
    286         #wpfooter {
    287             position: unset;
    288         }
    289         code {
    290             font-family: monospace;
    291             display: inherit;
    292         }
    293     </style>
    294 
     262
     263    </div>
     264    </div>
     265    </div>
    295266
    296267    <?php
    297268}
    298269
    299 add_action( 'admin_menu', 'shand_shorthand_menu' );
    300 
    301 ?>
     270function registerStyles() {
     271    // Adding styles.
     272    $css_path = '../css/options.css';
     273    wp_register_style( 'options_style', plugin_dir_url( __FILE__ ) . $css_path, array(), '1.3', 'all' );
     274    wp_enqueue_style( 'options_style' );
     275}
     276
     277add_action( 'init', 'registerStyles' );
     278
  • shorthand-connect/tags/1.3.32/index.php

    r2898299 r3258349  
    1 <?php // Silence is golden
     1<?php
     2// Silence is golden.
  • shorthand-connect/tags/1.3.32/shorthand-connect.php

    r3095659 r3258349  
    11<?php
    22/**
     3 * Main plugin file.
     4 *
    35 * @package Shorthand Connect
    4  * @version 1.3.31
    56 */
    67
     
    1011Description: Import your Shorthand stories into your WordPress CMS as simply as possible - magic!
    1112Author: Shorthand
    12 Version: 1.3.31
     13Version: 1.3.32
    1314Author URI: http://shorthand.com
    1415*/
    1516
    16 if ( file_exists( plugin_dir_path( __FILE__ ) . 'config.php' ) ) {
    17     include_once plugin_dir_path( __FILE__ ) . 'config.php';
     17// Make sure we don't expose any info if called directly.
     18if ( ! function_exists( 'add_action' ) ) {
     19    echo 'Hi there!  I\'m just a plugin, not much I can do when called directly.';
     20    exit;
     21}
     22
     23define( 'SHORTHAND_VERSION', '1.3.32' );
     24define( 'SHORTHAND__MINIMUM_WP_VERSION', '5.8' );
     25define( 'SHORTHAND__PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
     26
     27require_once SHORTHAND__PLUGIN_DIR . 'class-shorthand.php';
     28
     29// Add config.
     30if ( file_exists( SHORTHAND__PLUGIN_DIR . 'config.php' ) ) {
     31    include_once SHORTHAND__PLUGIN_DIR . 'config.php';
    1832} else {
    19     include_once plugin_dir_path( __FILE__ ) . 'config.default.php';
    20 }
    21 require_once plugin_dir_path( __FILE__ ) . 'includes/api.php';
    22 require_once plugin_dir_path( __FILE__ ) . 'includes/mass-pull.php';
    23 
    24 require_once plugin_dir_path( __FILE__ ) . 'includes/shorthand-options.php';
    25 require_once plugin_dir_path( __FILE__ ) . 'templates/abstract.php';
     33    include_once SHORTHAND__PLUGIN_DIR . 'config-default.php';
     34}
     35
     36require_once SHORTHAND__PLUGIN_DIR . 'includes/api.php';
     37require_once SHORTHAND__PLUGIN_DIR . 'includes/wp-api.php';
     38require_once SHORTHAND__PLUGIN_DIR . 'includes/mass-pull.php';
     39
     40if ( is_admin() || ( defined( 'WP_CLI' ) && WP_CLI ) ) {
     41    require_once SHORTHAND__PLUGIN_DIR . 'class-shorthand-admin.php';
     42    add_action( 'init', array( 'Shorthand_Admin', 'init' ) );
     43}
     44
     45// Loading initial setup or general configuration.
     46require_once SHORTHAND__PLUGIN_DIR . 'includes/shorthand-options-variables.php';
     47if ( isset( $_GET['view'] ) && ( $_GET['view'] === 'start' )) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
     48    require_once SHORTHAND__PLUGIN_DIR . 'includes/shorthand-options-init.php';
     49} else {
     50    require_once SHORTHAND__PLUGIN_DIR . 'includes/shorthand-options.php';
     51}
     52
     53// Added admin menu.
     54function shorthand_shorthand_menu() {
     55    add_options_page( 'Shorthand Options', 'Shorthand', 'manage_options', 'shorthand-options', 'shorthand_shorthand_options' );
     56}
     57add_action( 'admin_menu', 'shorthand_shorthand_menu' );
     58
     59require_once SHORTHAND__PLUGIN_DIR . 'templates/abstract.php';
    2660
    2761if ( ! function_exists( 'WP_Filesystem' ) ) {
     
    73107add_action( 'init', 'shand_create_post_type' );
    74108
    75 function shand_wpt_shorthand_story() {
     109function shorthand_wpt_shorthand_story() {
    76110    global $post;
    77     global $server_url;
    78111    global $show_archived_stories;
    79112    $baseurl = '';
    80113
    81     ?>
    82     <style>
    83     li.story {
    84         float: left;
    85         width: 160px;
    86         margin: 5px;
    87     }
    88 
    89     li.story.selected label {
    90         background: #5b9dd9;
    91         color: #eee;
    92     }
    93 
    94     li.story input {
    95         display: none !important;
    96     }
    97 
    98     li.story label {
    99         display: block;
    100         background: #fafafa;
    101         text-align: center;
    102         height: 160px;
    103         padding-top: 5px;
    104         border: 1px solid #eeeeee;
    105     }
    106 
    107     li.story label:hover {
    108         border: 1px solid #5b9dd9;
    109     }
    110 
    111     li.story label span {
    112         display: block;
    113         font-weight: bold;
    114     }
    115 
    116     li.story label span.desc {
    117         display: none;
    118     }
    119 
    120     div.clear {
    121         clear: left;
    122     }
    123 
    124     div.publishing-actions {
    125         padding: 10px;
    126         clear: both;
    127         border-top: 1px solid #dcdcde;
    128         background: #f6f7f7;
    129         margin: -12px;
    130         margin-top: -12px;
    131         margin-top: 10px;
    132         display: flex;
    133         justify-content: center;
    134     }
    135 
    136     .button-shorthand {
    137         background-color: #000;
    138         border: 1px solid #000;
    139         transition: background-color .15s ease, color .15s ease;
    140         color: #fff;
    141         font-family: poppins, sans-serif;
    142         font-size: 13px;
    143         font-weight: 600;
    144         letter-spacing: 0;
    145         line-height: 1;
    146         padding: 1em 2em;
    147         text-decoration: none;
    148         cursor: pointer;
    149         border-radius: .9em;
    150     }
    151 
    152     .button-shorthand:hover {
    153         background-color: #fff;
    154         border-color: #000;
    155         color: #000;
    156     }
    157 
    158     .button-shorthand:disabled {
    159         pointer-events: none;
    160     }
    161 
    162     #codearea {
    163         border: 1px solid #999999;
    164         font-family: Consolas, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace;
    165         width: 100%;
    166         height: 300px;
    167     }
    168 
    169     #abstract {
    170         border: 1px solid #999999;
    171         width: 100%;
    172         height: 200px;
    173     }
    174 
    175     ul.stories {
    176         max-height: 400px;
    177         overflow-y: scroll;
    178     }
    179 
    180     ul.stories img {
    181         object-fit: cover;
    182         height: 110px;
    183     }
    184 
    185     p.warning {
    186         color: red;
    187         font-weight: bold;
    188     }
    189     </style>
    190     <?php
     114    // Adding styles.
     115    $css_path = 'css/connect.css';
     116    wp_register_style( 'connect_style', plugin_dir_url( __FILE__ ) . $css_path, array(), '1.3', 'all' );
     117    wp_enqueue_style( 'connect_style' );
    191118
    192119    $selected_story = get_post_meta( $post->ID, 'story_id', true );
     
    195122        return;
    196123    }
    197     $stories = sh_get_stories();
    198     $profile = sh_get_profile();
     124
     125    $profile = shorthand_api_get_profile();
    199126
    200127    if ( ! ( $profile ) ) {
    201         echo 'Could not connect to Shorthand, please check your API token in <a href="options-general.php?page=shorthand-options">Shorthand settings</a>.';
    202     } elseif ( $stories === null ) {
    203         echo 'You currently have no stories ready for publishing on Shorthand. Please check that your story is set to be ready for publishing.';
     128        $uri = Shorthand_Admin::get_page_url();
     129        printf( wp_kses( __( 'Could not connect to Shorthand, please check your API token in <a alt="(opens Shorthand Connect plugin settings)" href="%s">Shorthand settings</a>.' ) ), esc_url( $uri ) );
    204130    } else {
    205         echo '<ul class="stories">';
    206         foreach ( $stories as $story ) {
    207             $selected       = '';
    208             $story_selected = '';
    209             if ( $selected_story === $story->id ) {
    210                 $selected       = 'checked';
    211                 $story_selected = 'selected';
    212             }
    213             $archived = '';
    214             if ( isset( $story->story_version ) && '1' === $story->story_version ) {
    215                 if ( $show_archived_stories ) {
    216                     $archived = ' (archived)';
    217                 } else {
    218                     continue;
    219                 }
    220             }
    221             echo '<li class="story ' . esc_attr( $story_selected ) . '"><label><input name="story_id" type="radio" value="' . esc_attr( $story->id ) . '" ' . esc_html( $selected ) . ' /><img width="150" src="' . esc_url( $baseurl . $story->image ) . '" /><span class="title">' . esc_html( $story->title . $archived ) . '</span><span class="desc">' . esc_html( $story->metadata->description ) . '</span></a></label></li>';
    222         }
    223         echo '</ul><div class="clear"></div>';
     131        ?>
     132    <div id="stories-list">
     133    <div class="filter-container wp-core-ui">
     134        <input class="search" placeholder="Search for a story" />
     135        <span class="sort button-secondary desc" data-sort="updated_value">
     136        Edited<span class="sort-icon"></span>
     137        </span>
     138    </div>
     139    <!-- List to populate the stories. -->
     140    <ul class="list stories">
     141
     142    </ul>
     143    </div>
     144        <?php
     145        wp_register_script(
     146            'list-js',
     147            plugin_dir_url( __FILE__ ) . 'js/list.js/v2.3.4/list.min.js',
     148            array(),
     149            '2.3.4',
     150            array()
     151        );
     152        wp_enqueue_script( 'list-js' );
     153
     154        wp_register_script(
     155            'list-js-stories',
     156            plugin_dir_url( __FILE__ ) . 'js/connect-stories-list.js',
     157            array(),
     158            '1.0.0',
     159            array(
     160                'strategy' => 'defer',
     161            )
     162        );
     163        wp_enqueue_script( 'list-js-stories' );
     164
     165        wp_register_script(
     166            'list-js-stories-selected',
     167            plugin_dir_url( __FILE__ ) . 'js/connect-stories-list-selected.js',
     168            array(),
     169            '1.0.0',
     170            array(
     171                'strategy' => 'defer',
     172            )
     173        );
     174        wp_enqueue_script( 'list-js-stories-selected' );
     175        wp_register_script(
     176            'fetch-stories',
     177            plugin_dir_url( __FILE__ ) . 'js/connect-stories-fetch.js',
     178            array(),
     179            '1.0.0',
     180            array(
     181                'strategy' => 'defer',
     182            )
     183        );
     184        wp_localize_script('fetch-stories', 'wp_server', array(
     185            'url' => ( get_option('permalink_structure') ) ? "/wp-json/shorthand_connect/v1/stories/" : "/?rest_route=/shorthand_connect/v1/stories/",
     186            'nonce' => wp_create_nonce('wp_rest'),
     187            'selected_story' => $selected_story
     188        ));
     189        wp_enqueue_script( 'fetch-stories' );
     190        echo '<div class="clear"></div>';
    224191    }
    225192
    226193    // Noncename needed to verify where the data originated
    227     echo '<input type="hidden" name="eventmeta_noncename" id="eventmeta_noncename" value="' .
    228     esc_attr( wp_create_nonce( plugin_basename( __FILE__ ) ) ) . '" />';
    229 
     194    echo '<input type="hidden" name="eventmeta_noncename" id="eventmeta_noncename" value="' . esc_attr( wp_create_nonce( plugin_basename( __FILE__ ) ) ) . '" />';
     195}
     196
     197function shorthand_wpt_shorthand_story_advanced() {
    230198    ?>
     199    <div class="form-check form-switch text-light">
     200    <input class="form-check-input" type="checkbox" id="show-advanced">
     201    <label class="form-check-label" for="show-advanced">Show advanced options</label>
     202    </div>
    231203    <script>
    232     jQuery('li.story input:radio').click(function () {
    233         jQuery('li.story').removeClass('selected');
    234         jQuery(this).parent().parent().addClass('selected');
    235         jQuery('label#title-prompt-text').text('');
    236         jQuery('input#title').val(jQuery(this).parent().find('span.title').text());
    237         if (jQuery('textarea#abstract').val() === '') {
    238         jQuery('textarea#abstract').val(jQuery(this).parent().find('span.desc').text());
    239         }
    240     });
     204    //var elements = document.querySelectorAll( 'body *' );
     205    //document.getElementById("postcustom").style.visibility = 0;
     206    console.log(document.getElementById("postcustom"));
    241207    </script>
    242208    <?php
     
    259225        formaction="?shand_update"
    260226    />
    261 
    262     <script>
    263         jQuery('#post').submit(function () {
    264         jQuery('#shorthand_update').prop('disabled', true);
    265         });
    266     </script>
    267 
    268227    </div>
    269228
    270229    <?php
     230
     231    wp_register_script(
     232        'connect-form',
     233        plugin_dir_url( __FILE__ ) . 'js/connect-form.js',
     234        array(),
     235        '1.0.0',
     236        array(
     237            'strategy' => 'defer',
     238        )
     239    );
     240    wp_enqueue_script( 'connect-form' );
    271241}
    272242
     
    275245    global $noabstract;
    276246    $selected_story = get_post_meta( $post->ID, 'story_id', true );
     247
    277248    if ( $selected_story ) {
    278249        add_meta_box(
    279             'shand_wpt_shorthand_story_update',
     250            'shorthand_wpt_shorthand_story_update',
    280251            'Update Shorthand Story',
    281             'shand_wpt_shorthand_story',
     252            'shorthand_wpt_shorthand_story',
    282253            'shorthand_story',
    283254            'side',
     
    286257    } else {
    287258        add_meta_box(
    288             'shand_wpt_shorthand_story',
     259            'shorthand_wpt_shorthand_story',
    289260            'Select Shorthand Story',
    290             'shand_wpt_shorthand_story',
     261            'shorthand_wpt_shorthand_story',
    291262            'shorthand_story',
    292263            'normal',
    293             'default'
    294         );
    295     }
     264            'high'
     265        );
     266    }
     267
    296268    if ( ! $noabstract ) {
    297269        add_meta_box(
     
    300272            'shand_wpt_shorthand_abstract',
    301273            'shorthand_story',
    302             'normal',
    303             'default'
    304         );
    305     }
    306     add_meta_box( 'shand_wpt_shorthand_extra_html', 'Add additional HTML', 'shand_wpt_shorthand_extra_html', 'shorthand_story', 'normal', 'default' );
     274            'advanced'
     275        );
     276    }
     277
     278    add_meta_box(
     279        'shand_wpt_shorthand_extra_html',
     280        'Add additional HTML',
     281        'shand_wpt_shorthand_extra_html',
     282        'shorthand_story',
     283        'advanced'
     284    );
    307285}
    308286
     
    323301
    324302/* Save the shorthand story */
    325 function shand_save_shorthand_story( $post_id, $post ) {
     303function shorthand_save_shorthand_story( $post_id, $post ) {
     304    $profile = shorthand_api_get_profile();
     305    if ( ( get_post_type( $post ) === 'shorthand_story' ) && ! ( $profile ) ) {
     306        $uri = Shorthand_Admin::get_page_url();
     307        wp_die( message: sprintf( wp_kses_post ( __( 'Could not connect to Shorthand, please check your API token in <a alt="(opens Shorthand Connect plugin settings)" href="%s">Shorthand settings</a>.', 'shorthand-connect' ) ), esc_url( $uri ) ), title: wp_kses_post( __( 'Shorthand is not connected' ) ) );
     308    }
     309
    326310    WP_Filesystem();
    327311    global $wp_filesystem;
     
    363347        // Sanitize but also check if the query is GET or POST.
    364348        if ( isset( $_REQUEST['story_id'] ) ) {
    365             $story_id = filter_input( INPUT_GET, 'story_id', FILTER_SANITIZE_STRING );
    366             // If the variable is not present in the $_GET array.
    367             if ( null === $story_id ) {
    368                 $story_id = filter_input( INPUT_POST, 'story_id', FILTER_SANITIZE_STRING );
    369             }
     349            $story_id      =  sanitize_text_field( $_REQUEST['story_id'] );
    370350            $safe_story_id = preg_replace( '/\W|_/', '', $story_id );
    371351        }
     
    429409            if ( ! $noabstract ) {
    430410                $abstract = $body;
    431                 remove_action( 'save_post', 'shand_save_shorthand_story', 10, 3 );
     411                remove_action( 'save_post', 'shorthand_save_shorthand_story', 10, 3 );
    432412                $post = array(
    433413                    'ID'           => $post_id,
    434                     'post_content' => shand_abstract_template( $post_id, wp_kses_post( $_REQUEST['abstract'] ), $abstract ),
     414                    'post_content' => shorthand_abstract_template( $post_id, wp_kses_post( $_REQUEST['abstract'] ), $abstract ),
    435415                );
    436416                wp_update_post( $post );
    437                 add_action( 'save_post', 'shand_save_shorthand_story', 10, 3 );
     417                add_action( 'save_post', 'shorthand_save_shorthand_story', 10, 3 );
    438418            } else {
    439                 remove_action( 'save_post', 'shand_save_shorthand_story', 10, 3 );
     419                remove_action( 'save_post', 'shorthand_save_shorthand_story', 10, 3 );
    440420                $post = array(
    441421                    'ID'           => $post_id,
     
    443423                );
    444424                wp_update_post( $post );
    445                 add_action( 'save_post', 'shand_save_shorthand_story', 10, 3 );
     425                add_action( 'save_post', 'shorthand_save_shorthand_story', 10, 3 );
    446426            }
    447427
     
    461441}
    462442
    463 add_action( 'save_post', 'shand_save_shorthand_story', 10, 3 );
     443add_action( 'save_post', 'shorthand_save_shorthand_story', 10, 3 );
    464444
    465445/* Load Shorthand Template Hook */
     
    467447    global $post;
    468448    if ( 'shorthand_story' === $post->post_type ) {
    469         $path = locate_template( array( 'single-shorthand_story.php', 'templates/single-shorthand_story.php', 'template-parts/single-shorthand_story.php' ) );
     449        $path = locate_template(
     450            array(
     451                'single-shorthand_story.php',
     452                'templates/single-shorthand_story.php',
     453                'template-parts/single-shorthand_story.php',
     454                'single-shorthand-story.php',
     455                'templates/single-shorthand-story.php',
     456                'template-parts/single-shorthand-story.php',
     457            )
     458        );
    470459        if ( $path ) {
    471460            return $path;
    472461        }
    473462        $plugin_path   = plugin_dir_path( __FILE__ );
    474         $template_name = 'templates/single-shorthand_story.php';
     463        $template_name = 'templates/single-shorthand-story.php';
    475464        if ( ( get_stylesheet_directory() . '/' . $template_name === $template )
    476465            || ! file_exists( $plugin_path . $template_name )
     
    491480    if ( is_single() && 'shorthand_story' === get_post_type() ) {
    492481        $meta = get_post_meta( get_post()->ID );
    493         echo get_shorthandinfo( $meta, 'story_head' );
     482        echo ( get_shorthandinfo( $meta, 'story_head' ) );  // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
    494483    }
    495484}
     
    506495    if ( $query->is_main_query() && ! is_admin() ) {
    507496        $queried_object      = get_queried_object();
    508         $shorthand_templates = array( 'single-shorthand_story.php', 'templates/single-shorthand_story.php', 'template-parts/single-shorthand_story.php' );
     497        $shorthand_templates = array(
     498            'single-shorthand_story.php',
     499            'templates/single-shorthand_story.php',
     500            'template-parts/single-shorthand_story.php',
     501            'single-shorthand-story.php',
     502            'templates/single-shorthand-story.php',
     503            'template-parts/single-shorthand-story.php',
     504        );
    509505
    510506        // Check if the queried object uses a Shorthand Post template from the array.
     
    555551 * Activates plugin by creating post type.
    556552 */
    557 function shand_shorthand_activate() {
     553function shorthand_shorthand_activate() {
     554
    558555    shand_create_post_type();
    559     flush_rewrite_rules();
    560 }
    561 
    562 register_activation_hook( __FILE__, 'shand_shorthand_activate' );
     556    flush_rewrite_rules(); // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.flush_rewrite_rules_flush_rewrite_rules
     557
     558    // Set config values.
     559    update_option( 'sh_v2_token', '' );
     560    update_option( 'sh_permalink', 'shorthand_story' );
     561    $css_path = '/css/options-default.css';
     562    $sh_css   = file_get_contents( __DIR__. $css_path ); // phpcs:ignore WordPressVIPMinimum.Performance.FetchingRemoteData.FileGetContentsUnknown
     563    update_option( 'sh_css', $sh_css );
     564    update_option( 'sh_regex_list', '' );
     565    update_option( 'sh_media_cron_offload', true );
     566    update_option( 'sh_disable_acf', false );
     567}
     568
     569register_activation_hook( __FILE__, 'shorthand_shorthand_activate' );
    563570register_deactivation_hook( __FILE__, 'flush_rewrite_rules' );
    564571
  • shorthand-connect/tags/1.3.32/templates/abstract.php

    r3095659 r3258349  
    11<?php
     2/**
     3 * Shorthand abstract template.
     4 *
     5 * @package Shorthand Connect
     6 */
    27
    3 function shand_abstract_template( $post_id, $abstract_html, $data ) {
    4     $abstract_html = '<p>' . $abstract_html . '</p>';
    5 
    6     $abstract_html .= '<a href="' . get_permalink( $post_id ) . '">View the story</a>';
    7 
    8     $abstract_html .= '<div style="display:none;">' . $data . '</div>';
    9     return $abstract_html;
     8/**
     9 * Generates a shorthand abstract template for a post.
     10 *
     11 * Creates a basic abstract template containing the provided abstract HTML,
     12a link to the post, and hidden data.
     13 *
     14 * @param int    $post_id The ID of the post.
     15 * @param string $abstract_html The HTML content for the abstract.
     16 * @param string $data Additional data to be included in a hidden div.
     17 * @return string The generated abstract HTML.
     18 */
     19function shorthand_abstract_template( $post_id, $abstract_html, $data ) {
     20    $title    = __( 'View the story' );
     21    $abstract = array(
     22        '<p>' . $abstract_html . '</p>',
     23        '<a href="' . get_permalink( $post_id ) . '">' . $title . '</a>',
     24        '<div style="display:none;">' . $data . '</div>',
     25    );
     26    return implode( $abstract );
    1027}
  • shorthand-connect/tags/1.3.32/trunk/includes/api.php

    r3095659 r3258349  
    11<?php
     2/**
     3 * Shorthand API functions.
     4 *
     5 * @package Shorthand Connect
     6 */
    27
    38/*
     
    1116 * @param array $options An array of extra options args to pass to wp_remote_request
    1217 */
    13 function sh_v2_api_request( $url, $options ) {
     18function shorthand_api_v2_request( $url, $options ) {
    1419    $token = get_option( 'sh_v2_token' );
    1520    if ( ! $token ) {
     
    4449}
    4550
    46 function sh_v2_api_request_json( $url, $options ) {
    47     $response = sh_v2_api_request( $url, $options );
     51/**
     52 * Makes a JSON API request to the shorthand API.
     53 *
     54 * @param string $url The URL of the API endpoint.
     55 * @param array  $options Optional arguments for the API request.
     56 * @return mixed The decoded JSON response, or null if an error occurs.
     57 */
     58function shorthand_api_request_json( $url, $options ) {
     59    $response = shorthand_api_v2_request( $url, $options );
    4860    $body     = wp_remote_retrieve_body( $response );
    4961    return json_decode( $body );
    5062}
    5163
    52 function sh_get_profile() {
     64/**
     65 * Retrieves user profile information from the shorthand API.
     66 *
     67 * @return object User profile information, including username, gravatar, and other potential data.
     68 */
     69function shorthand_api_get_profile() {
    5370    $tokeninfo = array();
    5471
    55     $data = sh_v2_api_request_json( '/v2/token-info', array() );
     72    $data = shorthand_api_request_json( '/v2/token-info', array() );
    5673    if ( $data && isset( $data->organisation_id ) ) {
    5774        $tokeninfo['username'] = $data->name . ' (' . $data->token_type . ' Token)';
     
    6380}
    6481
    65 function sh_get_stories() {
     82/**
     83 * Retrieves stories from the shorthand API.
     84 *
     85 * Fetches a list of stories from the shorthand API, optionally filtered by keyword.
     86 *
     87 * @param string $keyword Optional keyword to filter stories by.
     88 * @return array|null An array of story data, or null if an error occurs or no stories are found.
     89 */
     90function shorthand_api_get_stories(  string $keyword = '', string $cursor = '', string $limit = '50' ) {
    6691    $stories = null;
    67 
    68     $data = sh_v2_api_request_json( '/v2/stories', array( 'timeout' => '240' ) );
     92    $url     = '/v2/stories';
     93    $url     .= '?limit='.$limit;
     94    if ( $cursor && $cursor != '') {
     95        $url .= '&cursor=' . $cursor;
     96    }
     97    if ( $keyword && $keyword != '' ) {
     98        $url .= '&keyword=' . $keyword;
     99    }
     100   
     101
     102    $data = shorthand_api_request_json( $url, array( 'timeout' => '240' ) );
    69103    if ( $data ) {
    70104        $stories = array();
    71         // Something went wrong
     105        // Something went wrong.
    72106        if ( isset( $data->status ) && $data->status ) {
    73107            return null;
    74108        }
     109
    75110        foreach ( $data as $storydata ) {
    76             $story     = array(
    77                 'image'         => $storydata->signedCover,
    78                 'id'            => $storydata->id,
    79                 'metadata'      => (object) array(
    80                     'description' => $storydata->description,
    81                 ),
    82                 'title'         => $storydata->title,
    83                 'story_version' => '' . $storydata->version,
     111            $updated_timestamp   = strtotime( esc_html( $storydata->updatedAt ) );
     112            $updated_at          = $storydata->updatedAt;
     113            $published_timestamp = strtotime( esc_html( $storydata->lastPublishedAt ) );
     114            $updated             = human_time_diff( $updated_timestamp, current_time( 'timestamp' ) );
     115            $published           = human_time_diff( $published_timestamp, current_time( 'timestamp' ) );
     116            $stories[]           = array(
     117                'version_value'       => '' . $storydata->version,
     118                'title'               => $storydata->title,
     119                'description'         => $storydata->description,
     120                'imagealt'            => $storydata->title,
     121                'image'               => $storydata->signedCover,
     122                'updated_timestamp'   => $updated_timestamp,
     123                'updated_at'          => $updated_at,
     124                'updated_value'       => $updated,
     125                'published_timestamp' => $published_timestamp,
     126                'published_value'     => $published,
     127                'story_id'            => $storydata->id,
     128                'class'               => 'story',
    84129            );
    85             $stories[] = (object) $story;
    86130        }
    87131    }
     
    90134}
    91135
     136/**
     137 * Gets the path to the story directory.
     138 *
     139 * Determines the path to the directory where story files for a specific post
     140 * and story ID should be stored.
     141 *
     142 * @param int    $post_id The ID of the post.
     143 * @param string $story_id The ID of the story.
     144 * @return string|null The path to the story directory, or null if the directory doesn't exist or an error occurs.
     145 */
    92146function sh_get_story_path( $post_id, $story_id ) {
    93147    init_wp_filesystem();
     
    95149    $destination_path = $destination['path'] . '/shorthand/' . $post_id . '/' . $story_id;
    96150
    97     // on WP VIP, folders in the uploads dir always exist
     151    // On WP VIP, folders in the uploads dir always exist.
    98152    if ( ! file_exists( $destination_path ) ) {
    99153        $destination_path = null;
     
    105159}
    106160
     161/**
     162 * Gets the URL of the story directory.
     163 *
     164 * Determines the URL of the directory where story files for a specific post
     165 * and story ID are located.
     166 *
     167 * @param int    $post_id The ID of the post.
     168 * @param string $story_id The ID of the story.
     169 * @return string The URL of the story directory.
     170 */
    107171function shorthand_get_story_url( $post_id, $story_id ) {
    108172    init_wp_filesystem();
     
    123187    $story            = array();
    124188
    125     // Attempt to connect to the server
    126     $zip_file          = wp_tempnam( 'sh_zip', $tmpdir );
    127     $response          = sh_v2_api_request(
     189    // Attempt to connect to the server.
     190    $zip_file = wp_tempnam( 'sh_zip', $tmpdir );
     191    $response = shorthand_api_v2_request(
    128192        '/v2/stories/' . $story_id . ( $without_assets ? '?without_assets=true' : '' ) . ( $assets_only ? '?assets_only=true' : '' ),
    129193        array(
     
    145209        );
    146210    } else {
    147         $story = extractStoryContent( $zip_file, $destination_path, $story_id );
     211        $story = extract_story_content( $zip_file, $destination_path, $story_id );
    148212    }
    149213
     
    153217}
    154218
    155 
     219/**
     220 * Initializes the WordPress filesystem.
     221 *
     222 * Ensures that the WordPress filesystem is available for file operations.
     223 * If not, it requests filesystem credentials and re-initializes the filesystem.
     224 */
    156225function init_wp_filesystem() {
    157226    WP_Filesystem();
     
    163232}
    164233
    165 function extractStoryContent( $zip_file, $destination_path, $story_id ) {
     234/**
     235 * Extracts story content from a ZIP file.
     236 *
     237 * Extracts the 'head.html' and 'article.html' files from a given ZIP file
     238 * into a specified destination path.
     239 *
     240 * @param string $zip_file The path to the ZIP file.
     241 * @param string $destination_path The path to the extraction destination.
     242 * @param string $story_id The ID of the story.
     243 * @return array An array containing extracted content, path, or error information.
     244 */
     245function extract_story_content( $zip_file, $destination_path, $story_id ) {
    166246    // WP VIP HOSTING COMPATIBILITY
    167247    $zip = new ZipArchive();
  • shorthand-connect/tags/1.3.32/trunk/includes/mass-pull.php

    r3095659 r3258349  
    11<?php
    2 /*
    3  * Sets each custom meta field, should be paired with sh_copy_story().
     2/**
     3 * Bulk story pull functions.
    44 *
     5 * @package Shorthand Connect
     6 */
     7
     8/**
     9 * Updates a story for a given post.
     10 *
     11 * Fetches story data from the shorthand API, copies story files, updates post
     12 * metadata, and performs post-processing on the story content.
    513 * Note: Unzipping a fresh story copy won't update the fields.
     14 *
     15 * @param int    $post_id The ID of the post.
     16 * @param string $story_id The ID of the story.
    617 */
    718function shand_update_story( $post_id, $story_id ) {
     
    1425
    1526    // Grab JSON for Story & Set into object $obj.
    16     $obj = sh_v2_api_request_json( '/v2/stories/' . $story_id . '/settings', array( 'timeout' => '240' ) );
     27    $obj = shorthand_api_request_json( '/v2/stories/' . $story_id . '/settings', array( 'timeout' => '240' ) );
    1728
    1829    $err = sh_copy_story( $post_id, $safe_story_id, $sh_media_cron_offload );
  • shorthand-connect/tags/1.3.32/trunk/includes/shorthand-options.php

    r3095659 r3258349  
    11<?php
     2/**
     3 * Admin settings for the first time dialog.
     4 *
     5 * @package Shorthand Connect
     6 */
     7
    28/* Options */
    3 function shand_shorthand_menu() {
    4     add_options_page( 'Shorthand Options', 'Shorthand', 'manage_options', 'shorthand-options', 'shand_shorthand_options' );
    5 }
    6 $default_sh_site_css = '
    7 /* START CSS FOR DEFAULT WP THEMES */
    8 .site {
    9     margin: 0;
    10     max-width: none;
    11 }
    12 .site-content {
    13     padding: 0 !important;
    14 }
    15 .site-inner {
    16     max-width: none;
    17 }
    18 .site-header {
    19     max-width: none;
    20     z-index: 100;
    21 }
    22 .site:before {
    23     width: 0;
    24 }
    25 /* END CSS FOR DEFAULT WP THEMES */
    26 ';
    27 
    28 // JSON Checker
    29 function validate_json( $json_string ) {
    30     // Try to decode the JSON data. If it fails, the JSON is invalid.
    31     $json_data = json_decode( $json_string, true );
    32 
    33     if ( json_last_error() !== JSON_ERROR_NONE ) {
    34         // The JSON is invalid.
    35         return false;
    36     }
    37 
    38     // Return the original JSON string if it's valid.
    39     return $json_string;
    40 }
    41 
    42 function shand_shorthand_options() {
    43     global $default_sh_site_css;
    44     global $server_url;
    45 
    46     // Rather than running a rewrite flush everytime a post is submitted, run it on plugin activate/deactivate
    47     function shand_rewrite_flush() {
    48         shand_create_post_type();
    49         flush_rewrite_rules();
    50     }
    51     register_activation_hook( __FILE__, 'shand_rewrite_flush' );
    52     register_deactivation_hook( __FILE__, 'flush_rewrite_rules' );
     9
     10// Redirect to the first time setup.
     11function shorthand_redirect_admin_config() {
     12    $profile = shorthand_api_get_profile();
     13    if ( isset( $_GET['page'] ) && ( 'shorthand-options' === $_GET['page'] ) && ( ! $profile ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
     14        $admin_url = Shorthand_Admin::get_page_url( 'init' );
     15        wp_redirect( $admin_url );
     16        die();
     17    }
     18}
     19add_action( 'admin_menu', 'shorthand_redirect_admin_config' );
     20
     21function shorthand_shorthand_options() {
     22    // Menu links.
     23    $menu_links = array(
     24        'token'        => 'API Key',
     25        'permalink'    => 'Permalinks',
     26        'css'          => 'Custom CSS',
     27        'processing'   => 'Post-processing',
     28        'experimental' => 'Experimental Features',
     29    );
     30
     31    // If current = all, then display all.
     32    $current  = isset( $_GET['navigation'] ) ? sanitize_key( $_GET['navigation'] ) : array_keys( $menu_links )[0];
     33    $profile  = shorthand_api_get_profile();
     34    $messages = array();
    5335
    5436    if ( ! current_user_can( 'manage_options' ) ) {
    5537        wp_die( esc_html( __( 'You do not have sufficient permissions to access this page.' ) ) );
    5638    }
     39
    5740    if ( isset( $_POST['sh_submit_hidden'] ) && 'Y' === $_POST['sh_submit_hidden'] && check_admin_referer( 'sh-update-configuration' ) ) {
    5841        // If there's a token set, use it, if not set it to an empty string
    5942        $sh_v2_token = isset( $_POST['sh_v2_token'] ) ? sanitize_text_field( $_POST['sh_v2_token'] ) : '';
    6043        update_option( 'sh_v2_token', $sh_v2_token );
     44
     45        $profile = shorthand_api_get_profile();
     46        if ( $profile ) {
     47            $messages['updated'] = '<p>' . SHORTHAND_CONFIG_STEP1_SUCCESS . '</p><p><strong>Username</strong>: ' . esc_html( $profile->username ) . '</p>';
     48        } else {
     49            $messages['notice-error'] = SHORTHAND_CONFIG_STEP1_ERROR;
     50        }
    6151    }
    6252
     
    6454
    6555    if ( isset( $_POST['sh_submit_hidden_two'] ) && 'Y' === $_POST['sh_submit_hidden_two'] && check_admin_referer( 'sh-update-configuration' ) ) {
    66         // Check if there's custom CSS, if there is, use wp_kses_post() to sanitize otherwise set an empty string
     56        // Check if there's custom CSS, if there is, use wp_kses_post()
     57        // to sanitize otherwise set an empty string.
    6758        $sh_css = isset( $_POST['sh_css'] ) ? wp_kses_post( $_POST['sh_css'] ) : '';
    6859        update_option( 'sh_css', $sh_css );
     60        $messages['updated'] = SHORTHAND_CONFIG_STEP3_SUCCESS;
    6961    }
    7062
    7163    if ( isset( $_POST['sh_submit_hidden_three'] ) && 'Y' === $_POST['sh_submit_hidden_three'] && check_admin_referer( 'sh-update-configuration' ) ) {
    72         // Check if there's custom permalink, if there is, use sanitize_text_field() to sanitize potential HTML and then set an empty string
     64        // Check if there's custom permalink, if there is, use sanitize_text_field()
     65        // to sanitize potential HTML and then set an empty string.
    7366        $sh_permalink = isset( $_POST['sh_permalink'] ) ? sanitize_text_field( $_POST['sh_permalink'] ) : '';
    7467        update_option( 'sh_permalink', $sh_permalink );
    75         shand_rewrite_flush();
     68        shorthand_rewrite_flush();
     69        $messages['updated'] = SHORTHAND_CONFIG_STEP2_SUCCESS;
    7670    }
    7771    $permalink_structure = esc_html( get_option( 'sh_permalink' ) );
     
    8276    }
    8377    $sh_css = get_option( 'sh_css' );
    84     $no_css = false;
    85     if ( '' === $sh_css ) {
    86         $no_css = true;
    87         if ( isset( $default_site_css ) ) {
    88             update_option( 'sh_css', $default_site_css );
    89         }
    90         $sh_css = $default_sh_site_css;
    91     }
    92 
    93     if ( isset( $_POST['sh_submit_hidden_four'] ) && 'Y' === $_POST['sh_submit_hidden_four'] && check_admin_referer( 'sh-update-configuration' ) ) {
    94         $sh_regex_list = isset( $_POST['sh_regex_list'] ) ? wp_unslash( $_POST['sh_regex_list'] ) : '';
    95 
     78
     79    if ( isset( $_POST['sh_submit_hidden_four'] ) &&
     80        ( 'Y' === $_POST['sh_submit_hidden_four'] ) &&
     81        check_admin_referer( 'sh-update-configuration' )
     82    ) {
     83        // sh_regex_list may contain <tags> for lookup and processing on import and so may need to include <script> etc; however it is only ever displayed within a text-area value and manually processed.
     84        $sh_regex_list = empty( $_POST['sh_regex_list'] ) ?  '' : wp_unslash( $_POST['sh_regex_list'] ); //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
     85
     86        $messages['updated'] = SHORTHAND_CONFIG_STEP4_SUCCESS;
    9687        if ( empty( $sh_regex_list ) ) {
    97             // Update the option with an empty value if the input is empty
     88            // Update the option with an empty value if the input is empty.
    9889            update_option( 'sh_regex_list', '' );
    9990        } else {
    10091            // Validate if it's a valid JSON without sanitizing
    101             $sh_regex_list = validate_json( $sh_regex_list );
     92            $sh_regex_list = Shorthand_Admin::validate_json( $sh_regex_list );
    10293
    10394            if ( false !== $sh_regex_list ) {
     
    10697            } else {
    10798                // Handle invalid JSON error here.
     99                unset( $messages['updated'] );
     100                $messages['notice-error'] = SHORTHAND_CONFIG_STEP4_ERROR;
    108101            }
    109102        }
     
    112105    $sh_regex_list = base64_decode( get_option( 'sh_regex_list' ) );
    113106
    114     // Experimental Settings
     107    // Experimental settings.
    115108    if ( isset( $_POST['sh_submit_hidden_experimental'] ) && 'Y' === $_POST['sh_submit_hidden_experimental'] && check_admin_referer( 'sh-update-configuration' ) ) {
    116109        $sh_media_cron_offload = isset( $_POST['sh_media_cron_offload'] ) ? filter_var( $_POST['sh_media_cron_offload'], FILTER_VALIDATE_BOOLEAN ) : false;
     
    118111        update_option( 'sh_media_cron_offload', $sh_media_cron_offload );
    119112        update_option( 'sh_disable_acf', $sh_disable_acf );
     113        $messages['updated'] = SHORTHAND_CONFIG_STEP5_SUCCESS;
    120114    }
    121115    $sh_media_cron_offload = filter_var( get_option( 'sh_media_cron_offload' ), FILTER_VALIDATE_BOOLEAN );
    122116    $sh_disable_acf        = filter_var( get_option( 'sh_disable_acf' ), FILTER_VALIDATE_BOOLEAN );
    123117
    124     $profile = sh_get_profile();
    125     ?>   
    126 <div class="container">
    127     <div class="py-1">
    128     <h1>Shorthand API Configuration</h1>
    129     <h2>Shorthand Connect Status</h2>
    130     <form name="formtoken" method="post">
    131     <?php wp_nonce_field( 'sh-update-configuration' ); ?>
    132         <input type="hidden" name="sh_submit_hidden" value="Y" />
    133         <table class="form-table"><tbody>
    134         <tr class="v2row">
    135         <th scope="row"><label for="sh_v2_token"><?php esc_html_e( 'Shorthand Team Token', 'sh-v2-token' ); ?></label></th>
    136         <td><input type="text" id="sh_v2_token" name="sh_v2_token" value="<?php echo esc_attr( $v2_token ); ?>" size="28"></td>
    137         </tr>
    138         </tbody></table>
    139     <?php if ( $profile ) { ?>
    140         <p class="status">Successfully connected</p>
    141         <p><strong>Username</strong>: <?php echo esc_html( $profile->username ); ?></p>
    142     <?php } else { ?>
    143         <p class="status warn">Not Connected</p>
    144     <?php } ?>
    145     <div style='clear:both'></div>
    146         <p class="submit">
    147         <input type="submit" name="Submit" class="button-primary" value="<?php esc_attr_e( 'Save Changes' ); ?>" />
     118    ?>
     119    <div class="wrap shorthand-options-wrapper">
     120    <h1>Settings</h1>
     121
     122    <hr />
     123    <div class="shorthand-menu">
     124        <ol role="list">
     125        <?php foreach ( $menu_links as $menu_key => $menu_name ) { ?>
     126        <li class="py-1">
     127        <form name="menu_settings_<?php echo esc_attr( $menu_key ); ?>" method="post" action="<?php echo esc_url( Shorthand_Admin::get_page_url( 'config', esc_attr( $menu_key ) ) ); ?>">
     128            <input type="submit" name="Submit" class="button<?php echo ( in_array( $current, array( $menu_key, 'all' ) ) ) ? ' button button-primary' : ''; ?>" value="<?php esc_attr_e( $menu_name ); ?>" />
     129        </form>
     130        </li>
     131        <?php } ?>
     132        </ol>
     133    </div>
     134
     135    <div class="shorthand-form-wrapper">
     136    <form name="form_settings" method="post" action="<?php echo esc_url( Shorthand_Admin::get_page_url( 'config', esc_attr( $current ) ) ); ?>">
     137
     138        <?php if ( ! empty( $messages ) ) : ?>
     139            <?php foreach ( $messages as $class => $message ) : ?>
     140            <div class="notice <?php echo esc_html ( $class ); ?>"><?php echo wp_kses( $message, array(
     141    'p' => array(),
     142    'strong' => array(),
     143) ); ?></div>
     144            <?php endforeach; ?>
     145        <?php endif; ?>
     146
     147        <?php if ( in_array( $current, array( array_keys( $menu_links )[0], 'all' ) ) ) : ?>
     148        <div class="py-1">
     149            <h2>Shorthand API Key</h2>
     150            <?php wp_nonce_field( 'sh-update-configuration' ); ?>
     151        <input type="hidden" name="sh_submit_hidden" value="Y"/>
     152        <p><label for="sh_v2_token"><?php esc_html_e( 'Your API key provided by Shorthand', 'shorthand-connect' ); ?></label></p>
     153        <input type="text" id="sh_v2_token" name="sh_v2_token" value="<?php echo esc_attr( $v2_token ); ?>">
     154        <p>
     155            <?php esc_html_e( 'Do not have API key?', 'shorthand-connect' ); ?> <a target="_blank" alt="(<?php esc_html_e( 'opens Shorthand Connect plugin settings', 'shorthand-connect' ); ?>)" href="https://support.shorthand.com/en/articles/62-programmatic-publishing-with-the-shorthand-api"><?php esc_html_e( 'Get one here', 'shorthand-connect' ); ?></a>
    148156        </p>
    149     </form>
    150     </div>
    151 
    152 
    153     <div class="py-1">
    154     <h2>Shorthand Permalink Structure</h2>
    155         <p>Use this to set the permalink structure of Shorthand story URLs</p>
    156         <form name="permalinks" method="post">
    157     <?php wp_nonce_field( 'sh-update-configuration' ); ?>
    158             <input type="hidden" name="sh_submit_hidden_three" value="Y" />
    159             <p>
    160                 <?php _e( 'Permalink structure:', 'sh-permalink-value' ); ?><br /><?php echo esc_url( get_site_url() ); ?>/<input type="text" name="sh_permalink" value="<?php echo esc_attr( $permalink_structure ); ?>" size="20">/{STORY_NAME}
    161             </p>
    162             <p class="submit">
    163                 <input type="submit" name="Submit" class="button-primary" value="<?php esc_attr_e( 'Save Changes' ); ?>" />
    164             </p>
    165         </form>
    166     </div>
    167 
    168     <div class="py-1">
    169     <h2>Shorthand Story Page CSS (theme wide CSS)</h2>
    170         <p>Use this CSS to customise Shorthand Story pages to better suit your theme</p>
    171     <?php if ( $no_css ) { ?>
    172             <p class="status warn">No custom CSS found, using default theme CSS</p>
    173     <?php } ?>
    174         <form name="themecss" method="post">
    175     <?php wp_nonce_field( 'sh-update-configuration' ); ?>
    176             <input type="hidden" name="sh_submit_hidden_two" value="Y" />
    177             <textarea rows="10" cols="80" name="sh_css"><?php echo esc_textarea( $sh_css ); ?></textarea>
    178             <p class="submit">
    179                 <input type="submit" name="Submit" class="button-primary" value="<?php esc_attr_e( 'Save Changes' ); ?>" />
    180             </p>
    181         </form>
    182         </div>
    183 
    184     <div class="py-1">   
    185     <h2>Post-processing</h2>
    186         <p>Use this to create a JSON object of regex queries and replacements.</p>
     157            <div style='clear:both'></div>
     158        </div>
     159        <?php endif; ?>
     160
     161        <?php if ( in_array( $current, array( array_keys( $menu_links )[1], 'all' ) ) ) : ?>
     162        <div class="py-1">
     163        <h2>Permalink Structure</h2>
     164            <?php wp_nonce_field( 'sh-update-configuration' ); ?>
     165        <input type="hidden" name="sh_submit_hidden_three" value="Y"/>
     166        <p>
     167        <p><label for="sh_permalink"><?php esc_html_e( 'Set the permalink structure of Shorthand story URLs', 'shorthand-connect' ); ?></label></p>
     168        <input type="text" name="sh_permalink" value="<?php echo esc_attr( $permalink_structure ); ?>" size="20">
     169        <p><?php echo esc_url( get_site_url() ); ?>/<strong><?php echo esc_html( $permalink_structure ); ?></strong>/{STORY_NAME}</p>
     170        </div>
     171        <?php endif; ?>
     172
     173        <?php if ( in_array( $current, array( array_keys( $menu_links )[2], 'all' ) ) ) : ?>
     174        <div class="py-1">
     175        <h2>Custom CSS</h2>
     176        <p>Use theme wide CSS to customise Shorthand Story pages to better suit your theme</p>
     177            <?php wp_nonce_field( 'sh-update-configuration' ); ?>
     178        <input type="hidden" name="sh_submit_hidden_two" value="Y"/>
     179        <textarea rows="10" cols="80" name="sh_css"><?php echo esc_textarea( $sh_css ); ?></textarea>
     180        </div>
     181        <?php endif; ?>
     182
     183        <?php if ( in_array( $current, array( array_keys( $menu_links )[3], 'all' ) ) ) : ?>
     184        <div class="py-1">
     185        <h2>Post-processing</h2>
     186        <p>Create a JSON object of regex queries and replacements.</p>
    187187        <p><em>This Example removes title tags from within the head tag by replacing it with nothing.</em></p>
    188 <pre><code>
     188        <pre><code class="post-processing">
    189189{
    190190    "head": [
    191191    {
    192         &quot;query&quot;: &quot;/&lt;title&gt;(.*?)&lt;\\/title&gt;/&quot;,
    193         &quot;replace&quot;: &quot;&quot;
     192    &quot;query&quot;: &quot;/&lt;title&gt;(.*?)&lt;\\/title&gt;/&quot;,
     193    &quot;replace&quot;: &quot;&quot;
    194194    }
    195195    ],
    196196    "body": []
    197197}
    198 
    199 </code></pre>
    200         <form name="postprocessing" method="post">
    201     <?php wp_nonce_field( 'sh-update-configuration' ); ?>
    202             <input type="hidden" name="sh_submit_hidden_four" value="Y" />
    203             <textarea rows="10" cols="80" id="sh_regex_list" name="sh_regex_list"><?php echo esc_textarea( $sh_regex_list ); ?></textarea>
    204             <p class="submit">
    205                 <input type="submit" name="Submit" class="button-primary" value="<?php esc_attr_e( 'Save Changes' ); ?>" />
    206             </p>
    207         </form>
     198        </code></pre>
     199            <?php wp_nonce_field( 'sh-update-configuration' ); ?>
     200        <input type="hidden" name="sh_submit_hidden_four" value="Y"/>
     201        <textarea rows="10" cols="80" id="sh_regex_list"
     202        name="sh_regex_list"><?php echo esc_textarea( $sh_regex_list ); ?></textarea>
    208203        <script>
    209204            let textarea = document.querySelector("textarea#sh_regex_list");
    210        
    211             textarea.addEventListener("keyup", function(event) {
    212                 try {
    213                     JSON.parse(textarea.value);
     205           
     206            textarea.addEventListener("keyup", function (event) {
     207            try {
     208            textarea.value = JSON.stringify(JSON.parse(textarea.value), undefined, 4);
     209            textarea.setCustomValidity("");
     210
     211            } catch (err) {
     212                if (textarea.value != "") {
     213                    console.log("Invalid JSON");
     214                    textarea.setCustomValidity("Invalid JSON in the Post-processing field");
     215                } else {
    214216                    textarea.setCustomValidity("");
    215                    
    216                 } catch(err) {
    217                     if(textarea.value != ""){
    218                         console.log("Invalid JSON");
    219                         textarea.setCustomValidity("Invalid JSON in the Post-processing field");
    220                     } else {
    221                         textarea.setCustomValidity("");
    222                     }
    223217                }
    224                
     218            }
     219
    225220            });
    226221        </script>
    227     </div>
    228 
    229     <div class="py-1">
    230     <h2>Experimental Features</h2>
    231     <p>Early access features that are still subject to change.</p>
    232     <form name="form_experimental" method="post">
    233     <?php wp_nonce_field( 'sh-update-configuration' ); ?>
     222        </div>
     223        <?php endif; ?>
     224
     225        <?php if ( in_array( $current, array( array_keys( $menu_links )[4], 'all' ) ) ) : ?>
     226        <div class="py-1">
     227        <h2>Experimental Features <span class="badge badge-blue">Advanced</span></h2>
     228        <p>Early access features that are still subject to change.</p>
     229            <?php wp_nonce_field( 'sh-update-configuration' ); ?>
    234230        <input type="hidden" name="sh_submit_hidden_experimental" value="Y"/>
    235         <input type="checkbox" id="sh_media_cron_offload" name="sh_media_cron_offload"
    236             value="true" <?php echo esc_attr( $sh_media_cron_offload ? 'checked' : '' ); ?> />
    237         <label for="sh_media_cron_offload">Import media assets via cron</label>
    238         <p>Assets will be fetched after story save to prevent potential execution timeouts. Media won't be immediately
    239         available on save but progress will be updated based on the `media_status` field.</p>
    240         <p>It is advised that Shorthand Story Posts are saved as a draft first to trigger the cron job prior to public
    241         publishing.</p>
     231
     232        <div class="checkbox-container">
     233        <input type="checkbox" id="sh_media_cron_offload" name="sh_media_cron_offload" <?php echo esc_attr( $sh_media_cron_offload ? 'checked' : '' ); ?> />
     234        <div class="bordered">
     235            <label for="sh_media_cron_offload"><strong>Import media assets via cron</strong>
     236            <p>Assets will be fetched after story save to prevent potential execution timeouts. Media won't be immediately
     237            available on save but progress will be updated based on the `media_status` field.</p>
     238            <p>It is advised that Shorthand Story Posts are saved as a draft first to trigger the cron job prior to public
     239            publishing.</p>
     240            </label>
     241        </div>
     242        </div>
     243
    242244        <br/>
     245        <div class="checkbox-container">
    243246        <input type="checkbox" id="sh_disable_acf" name="sh_disable_acf"
    244             value="true" <?php echo esc_attr( $sh_disable_acf ? 'checked' : '' ); ?> />
    245         <label for="sh_disable_acf">Disable Advanced Custom Fields</label>
    246         <p>Used to prevent any potential issues that could cause the Shorthand Custom Fields to become hidden by Advanced
    247         Custom Fields.</p>
     247        value="true" <?php echo esc_attr( $sh_disable_acf ? 'checked' : '' ); ?> />
     248        <div class="bordered"><label for="sh_disable_acf"><strong>Disable Advanced Custom Fields</strong>
     249            <p>Used to prevent any potential issues that could cause the Shorthand Custom Fields to become hidden by Advanced Custom Fields.</p></label></div>
     250        </div>
    248251        </br>
     252        </div>
     253        <?php endif; ?>
     254
     255        <div class="py-1">
     256        <hr />
    249257        <p class="submit">
    250         <input type="submit" name="Submit" class="button-primary" value="<?php esc_attr_e( 'Save Changes' ); ?>"/>
     258            <input type="submit" name="Submit" class="button button-primary" value="<?php esc_attr_e( 'Save changes' ); ?>"/>
    251259        </p>
     260        </div>
    252261    </form>
    253     </div>
    254 </div>
    255     </div>
    256 <style>
    257         .py-1 {
    258             padding: 1em;
    259         }
    260         .bg-white {
    261             background: white;
    262         }
    263         .container {
    264             max-width: 980px;
    265         }
    266         img.grav {
    267             float: left;
    268             width:80px;
    269             margin-right:10px;
    270         }
    271         p.status {
    272             background:#dfd;
    273             color:green;
    274             font-weight:bold;
    275             width:350px;
    276             clear:left;
    277             padding:5px;
    278         }
    279         p.status.warn {
    280             background:#ffd;
    281             color:orange;
    282         }
    283         .row-hidden {
    284             display:none;
    285         }
    286         #wpfooter {
    287             position: unset;
    288         }
    289         code {
    290             font-family: monospace;
    291             display: inherit;
    292         }
    293     </style>
    294 
     262
     263    </div>
     264    </div>
     265    </div>
    295266
    296267    <?php
    297268}
    298269
    299 add_action( 'admin_menu', 'shand_shorthand_menu' );
    300 
    301 ?>
     270function registerStyles() {
     271    // Adding styles.
     272    $css_path = '../css/options.css';
     273    wp_register_style( 'options_style', plugin_dir_url( __FILE__ ) . $css_path, array(), '1.3', 'all' );
     274    wp_enqueue_style( 'options_style' );
     275}
     276
     277add_action( 'init', 'registerStyles' );
     278
  • shorthand-connect/tags/1.3.32/trunk/index.php

    r2898299 r3258349  
    1 <?php // Silence is golden
     1<?php
     2// Silence is golden.
  • shorthand-connect/tags/1.3.32/trunk/shorthand-connect.php

    r3095659 r3258349  
    11<?php
    22/**
     3 * Main plugin file.
     4 *
    35 * @package Shorthand Connect
    4  * @version 1.3.31
    56 */
    67
     
    1011Description: Import your Shorthand stories into your WordPress CMS as simply as possible - magic!
    1112Author: Shorthand
    12 Version: 1.3.31
     13Version: 1.3.32
    1314Author URI: http://shorthand.com
    1415*/
    1516
    16 if ( file_exists( plugin_dir_path( __FILE__ ) . 'config.php' ) ) {
    17     include_once plugin_dir_path( __FILE__ ) . 'config.php';
     17// Make sure we don't expose any info if called directly.
     18if ( ! function_exists( 'add_action' ) ) {
     19    echo 'Hi there!  I\'m just a plugin, not much I can do when called directly.';
     20    exit;
     21}
     22
     23define( 'SHORTHAND_VERSION', '1.3.32' );
     24define( 'SHORTHAND__MINIMUM_WP_VERSION', '5.8' );
     25define( 'SHORTHAND__PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
     26
     27require_once SHORTHAND__PLUGIN_DIR . 'class-shorthand.php';
     28
     29// Add config.
     30if ( file_exists( SHORTHAND__PLUGIN_DIR . 'config.php' ) ) {
     31    include_once SHORTHAND__PLUGIN_DIR . 'config.php';
    1832} else {
    19     include_once plugin_dir_path( __FILE__ ) . 'config.default.php';
    20 }
    21 require_once plugin_dir_path( __FILE__ ) . 'includes/api.php';
    22 require_once plugin_dir_path( __FILE__ ) . 'includes/mass-pull.php';
    23 
    24 require_once plugin_dir_path( __FILE__ ) . 'includes/shorthand-options.php';
    25 require_once plugin_dir_path( __FILE__ ) . 'templates/abstract.php';
     33    include_once SHORTHAND__PLUGIN_DIR . 'config-default.php';
     34}
     35
     36require_once SHORTHAND__PLUGIN_DIR . 'includes/api.php';
     37require_once SHORTHAND__PLUGIN_DIR . 'includes/wp-api.php';
     38require_once SHORTHAND__PLUGIN_DIR . 'includes/mass-pull.php';
     39
     40if ( is_admin() || ( defined( 'WP_CLI' ) && WP_CLI ) ) {
     41    require_once SHORTHAND__PLUGIN_DIR . 'class-shorthand-admin.php';
     42    add_action( 'init', array( 'Shorthand_Admin', 'init' ) );
     43}
     44
     45// Loading initial setup or general configuration.
     46require_once SHORTHAND__PLUGIN_DIR . 'includes/shorthand-options-variables.php';
     47if ( isset( $_GET['view'] ) && ( $_GET['view'] === 'start' )) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
     48    require_once SHORTHAND__PLUGIN_DIR . 'includes/shorthand-options-init.php';
     49} else {
     50    require_once SHORTHAND__PLUGIN_DIR . 'includes/shorthand-options.php';
     51}
     52
     53// Added admin menu.
     54function shorthand_shorthand_menu() {
     55    add_options_page( 'Shorthand Options', 'Shorthand', 'manage_options', 'shorthand-options', 'shorthand_shorthand_options' );
     56}
     57add_action( 'admin_menu', 'shorthand_shorthand_menu' );
     58
     59require_once SHORTHAND__PLUGIN_DIR . 'templates/abstract.php';
    2660
    2761if ( ! function_exists( 'WP_Filesystem' ) ) {
     
    73107add_action( 'init', 'shand_create_post_type' );
    74108
    75 function shand_wpt_shorthand_story() {
     109function shorthand_wpt_shorthand_story() {
    76110    global $post;
    77     global $server_url;
    78111    global $show_archived_stories;
    79112    $baseurl = '';
    80113
    81     ?>
    82     <style>
    83     li.story {
    84         float: left;
    85         width: 160px;
    86         margin: 5px;
    87     }
    88 
    89     li.story.selected label {
    90         background: #5b9dd9;
    91         color: #eee;
    92     }
    93 
    94     li.story input {
    95         display: none !important;
    96     }
    97 
    98     li.story label {
    99         display: block;
    100         background: #fafafa;
    101         text-align: center;
    102         height: 160px;
    103         padding-top: 5px;
    104         border: 1px solid #eeeeee;
    105     }
    106 
    107     li.story label:hover {
    108         border: 1px solid #5b9dd9;
    109     }
    110 
    111     li.story label span {
    112         display: block;
    113         font-weight: bold;
    114     }
    115 
    116     li.story label span.desc {
    117         display: none;
    118     }
    119 
    120     div.clear {
    121         clear: left;
    122     }
    123 
    124     div.publishing-actions {
    125         padding: 10px;
    126         clear: both;
    127         border-top: 1px solid #dcdcde;
    128         background: #f6f7f7;
    129         margin: -12px;
    130         margin-top: -12px;
    131         margin-top: 10px;
    132         display: flex;
    133         justify-content: center;
    134     }
    135 
    136     .button-shorthand {
    137         background-color: #000;
    138         border: 1px solid #000;
    139         transition: background-color .15s ease, color .15s ease;
    140         color: #fff;
    141         font-family: poppins, sans-serif;
    142         font-size: 13px;
    143         font-weight: 600;
    144         letter-spacing: 0;
    145         line-height: 1;
    146         padding: 1em 2em;
    147         text-decoration: none;
    148         cursor: pointer;
    149         border-radius: .9em;
    150     }
    151 
    152     .button-shorthand:hover {
    153         background-color: #fff;
    154         border-color: #000;
    155         color: #000;
    156     }
    157 
    158     .button-shorthand:disabled {
    159         pointer-events: none;
    160     }
    161 
    162     #codearea {
    163         border: 1px solid #999999;
    164         font-family: Consolas, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace;
    165         width: 100%;
    166         height: 300px;
    167     }
    168 
    169     #abstract {
    170         border: 1px solid #999999;
    171         width: 100%;
    172         height: 200px;
    173     }
    174 
    175     ul.stories {
    176         max-height: 400px;
    177         overflow-y: scroll;
    178     }
    179 
    180     ul.stories img {
    181         object-fit: cover;
    182         height: 110px;
    183     }
    184 
    185     p.warning {
    186         color: red;
    187         font-weight: bold;
    188     }
    189     </style>
    190     <?php
     114    // Adding styles.
     115    $css_path = 'css/connect.css';
     116    wp_register_style( 'connect_style', plugin_dir_url( __FILE__ ) . $css_path, array(), '1.3', 'all' );
     117    wp_enqueue_style( 'connect_style' );
    191118
    192119    $selected_story = get_post_meta( $post->ID, 'story_id', true );
     
    195122        return;
    196123    }
    197     $stories = sh_get_stories();
    198     $profile = sh_get_profile();
     124
     125    $profile = shorthand_api_get_profile();
    199126
    200127    if ( ! ( $profile ) ) {
    201         echo 'Could not connect to Shorthand, please check your API token in <a href="options-general.php?page=shorthand-options">Shorthand settings</a>.';
    202     } elseif ( $stories === null ) {
    203         echo 'You currently have no stories ready for publishing on Shorthand. Please check that your story is set to be ready for publishing.';
     128        $uri = Shorthand_Admin::get_page_url();
     129        printf( wp_kses( __( 'Could not connect to Shorthand, please check your API token in <a alt="(opens Shorthand Connect plugin settings)" href="%s">Shorthand settings</a>.' ) ), esc_url( $uri ) );
    204130    } else {
    205         echo '<ul class="stories">';
    206         foreach ( $stories as $story ) {
    207             $selected       = '';
    208             $story_selected = '';
    209             if ( $selected_story === $story->id ) {
    210                 $selected       = 'checked';
    211                 $story_selected = 'selected';
    212             }
    213             $archived = '';
    214             if ( isset( $story->story_version ) && '1' === $story->story_version ) {
    215                 if ( $show_archived_stories ) {
    216                     $archived = ' (archived)';
    217                 } else {
    218                     continue;
    219                 }
    220             }
    221             echo '<li class="story ' . esc_attr( $story_selected ) . '"><label><input name="story_id" type="radio" value="' . esc_attr( $story->id ) . '" ' . esc_html( $selected ) . ' /><img width="150" src="' . esc_url( $baseurl . $story->image ) . '" /><span class="title">' . esc_html( $story->title . $archived ) . '</span><span class="desc">' . esc_html( $story->metadata->description ) . '</span></a></label></li>';
    222         }
    223         echo '</ul><div class="clear"></div>';
     131        ?>
     132    <div id="stories-list">
     133    <div class="filter-container wp-core-ui">
     134        <input class="search" placeholder="Search for a story" />
     135        <span class="sort button-secondary desc" data-sort="updated_value">
     136        Edited<span class="sort-icon"></span>
     137        </span>
     138    </div>
     139    <!-- List to populate the stories. -->
     140    <ul class="list stories">
     141
     142    </ul>
     143    </div>
     144        <?php
     145        wp_register_script(
     146            'list-js',
     147            plugin_dir_url( __FILE__ ) . 'js/list.js/v2.3.4/list.min.js',
     148            array(),
     149            '2.3.4',
     150            array()
     151        );
     152        wp_enqueue_script( 'list-js' );
     153
     154        wp_register_script(
     155            'list-js-stories',
     156            plugin_dir_url( __FILE__ ) . 'js/connect-stories-list.js',
     157            array(),
     158            '1.0.0',
     159            array(
     160                'strategy' => 'defer',
     161            )
     162        );
     163        wp_enqueue_script( 'list-js-stories' );
     164
     165        wp_register_script(
     166            'list-js-stories-selected',
     167            plugin_dir_url( __FILE__ ) . 'js/connect-stories-list-selected.js',
     168            array(),
     169            '1.0.0',
     170            array(
     171                'strategy' => 'defer',
     172            )
     173        );
     174        wp_enqueue_script( 'list-js-stories-selected' );
     175        wp_register_script(
     176            'fetch-stories',
     177            plugin_dir_url( __FILE__ ) . 'js/connect-stories-fetch.js',
     178            array(),
     179            '1.0.0',
     180            array(
     181                'strategy' => 'defer',
     182            )
     183        );
     184        wp_localize_script('fetch-stories', 'wp_server', array(
     185            'url' => ( get_option('permalink_structure') ) ? "/wp-json/shorthand_connect/v1/stories/" : "/?rest_route=/shorthand_connect/v1/stories/",
     186            'nonce' => wp_create_nonce('wp_rest'),
     187            'selected_story' => $selected_story
     188        ));
     189        wp_enqueue_script( 'fetch-stories' );
     190        echo '<div class="clear"></div>';
    224191    }
    225192
    226193    // Noncename needed to verify where the data originated
    227     echo '<input type="hidden" name="eventmeta_noncename" id="eventmeta_noncename" value="' .
    228     esc_attr( wp_create_nonce( plugin_basename( __FILE__ ) ) ) . '" />';
    229 
     194    echo '<input type="hidden" name="eventmeta_noncename" id="eventmeta_noncename" value="' . esc_attr( wp_create_nonce( plugin_basename( __FILE__ ) ) ) . '" />';
     195}
     196
     197function shorthand_wpt_shorthand_story_advanced() {
    230198    ?>
     199    <div class="form-check form-switch text-light">
     200    <input class="form-check-input" type="checkbox" id="show-advanced">
     201    <label class="form-check-label" for="show-advanced">Show advanced options</label>
     202    </div>
    231203    <script>
    232     jQuery('li.story input:radio').click(function () {
    233         jQuery('li.story').removeClass('selected');
    234         jQuery(this).parent().parent().addClass('selected');
    235         jQuery('label#title-prompt-text').text('');
    236         jQuery('input#title').val(jQuery(this).parent().find('span.title').text());
    237         if (jQuery('textarea#abstract').val() === '') {
    238         jQuery('textarea#abstract').val(jQuery(this).parent().find('span.desc').text());
    239         }
    240     });
     204    //var elements = document.querySelectorAll( 'body *' );
     205    //document.getElementById("postcustom").style.visibility = 0;
     206    console.log(document.getElementById("postcustom"));
    241207    </script>
    242208    <?php
     
    259225        formaction="?shand_update"
    260226    />
    261 
    262     <script>
    263         jQuery('#post').submit(function () {
    264         jQuery('#shorthand_update').prop('disabled', true);
    265         });
    266     </script>
    267 
    268227    </div>
    269228
    270229    <?php
     230
     231    wp_register_script(
     232        'connect-form',
     233        plugin_dir_url( __FILE__ ) . 'js/connect-form.js',
     234        array(),
     235        '1.0.0',
     236        array(
     237            'strategy' => 'defer',
     238        )
     239    );
     240    wp_enqueue_script( 'connect-form' );
    271241}
    272242
     
    275245    global $noabstract;
    276246    $selected_story = get_post_meta( $post->ID, 'story_id', true );
     247
    277248    if ( $selected_story ) {
    278249        add_meta_box(
    279             'shand_wpt_shorthand_story_update',
     250            'shorthand_wpt_shorthand_story_update',
    280251            'Update Shorthand Story',
    281             'shand_wpt_shorthand_story',
     252            'shorthand_wpt_shorthand_story',
    282253            'shorthand_story',
    283254            'side',
     
    286257    } else {
    287258        add_meta_box(
    288             'shand_wpt_shorthand_story',
     259            'shorthand_wpt_shorthand_story',
    289260            'Select Shorthand Story',
    290             'shand_wpt_shorthand_story',
     261            'shorthand_wpt_shorthand_story',
    291262            'shorthand_story',
    292263            'normal',
    293             'default'
    294         );
    295     }
     264            'high'
     265        );
     266    }
     267
    296268    if ( ! $noabstract ) {
    297269        add_meta_box(
     
    300272            'shand_wpt_shorthand_abstract',
    301273            'shorthand_story',
    302             'normal',
    303             'default'
    304         );
    305     }
    306     add_meta_box( 'shand_wpt_shorthand_extra_html', 'Add additional HTML', 'shand_wpt_shorthand_extra_html', 'shorthand_story', 'normal', 'default' );
     274            'advanced'
     275        );
     276    }
     277
     278    add_meta_box(
     279        'shand_wpt_shorthand_extra_html',
     280        'Add additional HTML',
     281        'shand_wpt_shorthand_extra_html',
     282        'shorthand_story',
     283        'advanced'
     284    );
    307285}
    308286
     
    323301
    324302/* Save the shorthand story */
    325 function shand_save_shorthand_story( $post_id, $post ) {
     303function shorthand_save_shorthand_story( $post_id, $post ) {
     304    $profile = shorthand_api_get_profile();
     305    if ( ( get_post_type( $post ) === 'shorthand_story' ) && ! ( $profile ) ) {
     306        $uri = Shorthand_Admin::get_page_url();
     307        wp_die( message: sprintf( wp_kses_post ( __( 'Could not connect to Shorthand, please check your API token in <a alt="(opens Shorthand Connect plugin settings)" href="%s">Shorthand settings</a>.', 'shorthand-connect' ) ), esc_url( $uri ) ), title: wp_kses_post( __( 'Shorthand is not connected' ) ) );
     308    }
     309
    326310    WP_Filesystem();
    327311    global $wp_filesystem;
     
    363347        // Sanitize but also check if the query is GET or POST.
    364348        if ( isset( $_REQUEST['story_id'] ) ) {
    365             $story_id = filter_input( INPUT_GET, 'story_id', FILTER_SANITIZE_STRING );
    366             // If the variable is not present in the $_GET array.
    367             if ( null === $story_id ) {
    368                 $story_id = filter_input( INPUT_POST, 'story_id', FILTER_SANITIZE_STRING );
    369             }
     349            $story_id      =  sanitize_text_field( $_REQUEST['story_id'] );
    370350            $safe_story_id = preg_replace( '/\W|_/', '', $story_id );
    371351        }
     
    429409            if ( ! $noabstract ) {
    430410                $abstract = $body;
    431                 remove_action( 'save_post', 'shand_save_shorthand_story', 10, 3 );
     411                remove_action( 'save_post', 'shorthand_save_shorthand_story', 10, 3 );
    432412                $post = array(
    433413                    'ID'           => $post_id,
    434                     'post_content' => shand_abstract_template( $post_id, wp_kses_post( $_REQUEST['abstract'] ), $abstract ),
     414                    'post_content' => shorthand_abstract_template( $post_id, wp_kses_post( $_REQUEST['abstract'] ), $abstract ),
    435415                );
    436416                wp_update_post( $post );
    437                 add_action( 'save_post', 'shand_save_shorthand_story', 10, 3 );
     417                add_action( 'save_post', 'shorthand_save_shorthand_story', 10, 3 );
    438418            } else {
    439                 remove_action( 'save_post', 'shand_save_shorthand_story', 10, 3 );
     419                remove_action( 'save_post', 'shorthand_save_shorthand_story', 10, 3 );
    440420                $post = array(
    441421                    'ID'           => $post_id,
     
    443423                );
    444424                wp_update_post( $post );
    445                 add_action( 'save_post', 'shand_save_shorthand_story', 10, 3 );
     425                add_action( 'save_post', 'shorthand_save_shorthand_story', 10, 3 );
    446426            }
    447427
     
    461441}
    462442
    463 add_action( 'save_post', 'shand_save_shorthand_story', 10, 3 );
     443add_action( 'save_post', 'shorthand_save_shorthand_story', 10, 3 );
    464444
    465445/* Load Shorthand Template Hook */
     
    467447    global $post;
    468448    if ( 'shorthand_story' === $post->post_type ) {
    469         $path = locate_template( array( 'single-shorthand_story.php', 'templates/single-shorthand_story.php', 'template-parts/single-shorthand_story.php' ) );
     449        $path = locate_template(
     450            array(
     451                'single-shorthand_story.php',
     452                'templates/single-shorthand_story.php',
     453                'template-parts/single-shorthand_story.php',
     454                'single-shorthand-story.php',
     455                'templates/single-shorthand-story.php',
     456                'template-parts/single-shorthand-story.php',
     457            )
     458        );
    470459        if ( $path ) {
    471460            return $path;
    472461        }
    473462        $plugin_path   = plugin_dir_path( __FILE__ );
    474         $template_name = 'templates/single-shorthand_story.php';
     463        $template_name = 'templates/single-shorthand-story.php';
    475464        if ( ( get_stylesheet_directory() . '/' . $template_name === $template )
    476465            || ! file_exists( $plugin_path . $template_name )
     
    491480    if ( is_single() && 'shorthand_story' === get_post_type() ) {
    492481        $meta = get_post_meta( get_post()->ID );
    493         echo get_shorthandinfo( $meta, 'story_head' );
     482        echo ( get_shorthandinfo( $meta, 'story_head' ) );  // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
    494483    }
    495484}
     
    506495    if ( $query->is_main_query() && ! is_admin() ) {
    507496        $queried_object      = get_queried_object();
    508         $shorthand_templates = array( 'single-shorthand_story.php', 'templates/single-shorthand_story.php', 'template-parts/single-shorthand_story.php' );
     497        $shorthand_templates = array(
     498            'single-shorthand_story.php',
     499            'templates/single-shorthand_story.php',
     500            'template-parts/single-shorthand_story.php',
     501            'single-shorthand-story.php',
     502            'templates/single-shorthand-story.php',
     503            'template-parts/single-shorthand-story.php',
     504        );
    509505
    510506        // Check if the queried object uses a Shorthand Post template from the array.
     
    555551 * Activates plugin by creating post type.
    556552 */
    557 function shand_shorthand_activate() {
     553function shorthand_shorthand_activate() {
     554
    558555    shand_create_post_type();
    559     flush_rewrite_rules();
    560 }
    561 
    562 register_activation_hook( __FILE__, 'shand_shorthand_activate' );
     556    flush_rewrite_rules(); // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.flush_rewrite_rules_flush_rewrite_rules
     557
     558    // Set config values.
     559    update_option( 'sh_v2_token', '' );
     560    update_option( 'sh_permalink', 'shorthand_story' );
     561    $css_path = '/css/options-default.css';
     562    $sh_css   = file_get_contents( __DIR__. $css_path ); // phpcs:ignore WordPressVIPMinimum.Performance.FetchingRemoteData.FileGetContentsUnknown
     563    update_option( 'sh_css', $sh_css );
     564    update_option( 'sh_regex_list', '' );
     565    update_option( 'sh_media_cron_offload', true );
     566    update_option( 'sh_disable_acf', false );
     567}
     568
     569register_activation_hook( __FILE__, 'shorthand_shorthand_activate' );
    563570register_deactivation_hook( __FILE__, 'flush_rewrite_rules' );
    564571
  • shorthand-connect/tags/1.3.32/trunk/templates/abstract.php

    r3095659 r3258349  
    11<?php
     2/**
     3 * Shorthand abstract template.
     4 *
     5 * @package Shorthand Connect
     6 */
    27
    3 function shand_abstract_template( $post_id, $abstract_html, $data ) {
    4     $abstract_html = '<p>' . $abstract_html . '</p>';
    5 
    6     $abstract_html .= '<a href="' . get_permalink( $post_id ) . '">View the story</a>';
    7 
    8     $abstract_html .= '<div style="display:none;">' . $data . '</div>';
    9     return $abstract_html;
     8/**
     9 * Generates a shorthand abstract template for a post.
     10 *
     11 * Creates a basic abstract template containing the provided abstract HTML,
     12a link to the post, and hidden data.
     13 *
     14 * @param int    $post_id The ID of the post.
     15 * @param string $abstract_html The HTML content for the abstract.
     16 * @param string $data Additional data to be included in a hidden div.
     17 * @return string The generated abstract HTML.
     18 */
     19function shorthand_abstract_template( $post_id, $abstract_html, $data ) {
     20    $title    = __( 'View the story' );
     21    $abstract = array(
     22        '<p>' . $abstract_html . '</p>',
     23        '<a href="' . get_permalink( $post_id ) . '">' . $title . '</a>',
     24        '<div style="display:none;">' . $data . '</div>',
     25    );
     26    return implode( $abstract );
    1027}
  • shorthand-connect/trunk/includes/api.php

    r3095659 r3258349  
    11<?php
     2/**
     3 * Shorthand API functions.
     4 *
     5 * @package Shorthand Connect
     6 */
    27
    38/*
     
    1116 * @param array $options An array of extra options args to pass to wp_remote_request
    1217 */
    13 function sh_v2_api_request( $url, $options ) {
     18function shorthand_api_v2_request( $url, $options ) {
    1419    $token = get_option( 'sh_v2_token' );
    1520    if ( ! $token ) {
     
    4449}
    4550
    46 function sh_v2_api_request_json( $url, $options ) {
    47     $response = sh_v2_api_request( $url, $options );
     51/**
     52 * Makes a JSON API request to the shorthand API.
     53 *
     54 * @param string $url The URL of the API endpoint.
     55 * @param array  $options Optional arguments for the API request.
     56 * @return mixed The decoded JSON response, or null if an error occurs.
     57 */
     58function shorthand_api_request_json( $url, $options ) {
     59    $response = shorthand_api_v2_request( $url, $options );
    4860    $body     = wp_remote_retrieve_body( $response );
    4961    return json_decode( $body );
    5062}
    5163
    52 function sh_get_profile() {
     64/**
     65 * Retrieves user profile information from the shorthand API.
     66 *
     67 * @return object User profile information, including username, gravatar, and other potential data.
     68 */
     69function shorthand_api_get_profile() {
    5370    $tokeninfo = array();
    5471
    55     $data = sh_v2_api_request_json( '/v2/token-info', array() );
     72    $data = shorthand_api_request_json( '/v2/token-info', array() );
    5673    if ( $data && isset( $data->organisation_id ) ) {
    5774        $tokeninfo['username'] = $data->name . ' (' . $data->token_type . ' Token)';
     
    6380}
    6481
    65 function sh_get_stories() {
     82/**
     83 * Retrieves stories from the shorthand API.
     84 *
     85 * Fetches a list of stories from the shorthand API, optionally filtered by keyword.
     86 *
     87 * @param string $keyword Optional keyword to filter stories by.
     88 * @return array|null An array of story data, or null if an error occurs or no stories are found.
     89 */
     90function shorthand_api_get_stories(  string $keyword = '', string $cursor = '', string $limit = '50' ) {
    6691    $stories = null;
    67 
    68     $data = sh_v2_api_request_json( '/v2/stories', array( 'timeout' => '240' ) );
     92    $url     = '/v2/stories';
     93    $url     .= '?limit='.$limit;
     94    if ( $cursor && $cursor != '') {
     95        $url .= '&cursor=' . $cursor;
     96    }
     97    if ( $keyword && $keyword != '' ) {
     98        $url .= '&keyword=' . $keyword;
     99    }
     100   
     101
     102    $data = shorthand_api_request_json( $url, array( 'timeout' => '240' ) );
    69103    if ( $data ) {
    70104        $stories = array();
    71         // Something went wrong
     105        // Something went wrong.
    72106        if ( isset( $data->status ) && $data->status ) {
    73107            return null;
    74108        }
     109
    75110        foreach ( $data as $storydata ) {
    76             $story     = array(
    77                 'image'         => $storydata->signedCover,
    78                 'id'            => $storydata->id,
    79                 'metadata'      => (object) array(
    80                     'description' => $storydata->description,
    81                 ),
    82                 'title'         => $storydata->title,
    83                 'story_version' => '' . $storydata->version,
     111            $updated_timestamp   = strtotime( esc_html( $storydata->updatedAt ) );
     112            $updated_at          = $storydata->updatedAt;
     113            $published_timestamp = strtotime( esc_html( $storydata->lastPublishedAt ) );
     114            $updated             = human_time_diff( $updated_timestamp, current_time( 'timestamp' ) );
     115            $published           = human_time_diff( $published_timestamp, current_time( 'timestamp' ) );
     116            $stories[]           = array(
     117                'version_value'       => '' . $storydata->version,
     118                'title'               => $storydata->title,
     119                'description'         => $storydata->description,
     120                'imagealt'            => $storydata->title,
     121                'image'               => $storydata->signedCover,
     122                'updated_timestamp'   => $updated_timestamp,
     123                'updated_at'          => $updated_at,
     124                'updated_value'       => $updated,
     125                'published_timestamp' => $published_timestamp,
     126                'published_value'     => $published,
     127                'story_id'            => $storydata->id,
     128                'class'               => 'story',
    84129            );
    85             $stories[] = (object) $story;
    86130        }
    87131    }
     
    90134}
    91135
     136/**
     137 * Gets the path to the story directory.
     138 *
     139 * Determines the path to the directory where story files for a specific post
     140 * and story ID should be stored.
     141 *
     142 * @param int    $post_id The ID of the post.
     143 * @param string $story_id The ID of the story.
     144 * @return string|null The path to the story directory, or null if the directory doesn't exist or an error occurs.
     145 */
    92146function sh_get_story_path( $post_id, $story_id ) {
    93147    init_wp_filesystem();
     
    95149    $destination_path = $destination['path'] . '/shorthand/' . $post_id . '/' . $story_id;
    96150
    97     // on WP VIP, folders in the uploads dir always exist
     151    // On WP VIP, folders in the uploads dir always exist.
    98152    if ( ! file_exists( $destination_path ) ) {
    99153        $destination_path = null;
     
    105159}
    106160
     161/**
     162 * Gets the URL of the story directory.
     163 *
     164 * Determines the URL of the directory where story files for a specific post
     165 * and story ID are located.
     166 *
     167 * @param int    $post_id The ID of the post.
     168 * @param string $story_id The ID of the story.
     169 * @return string The URL of the story directory.
     170 */
    107171function shorthand_get_story_url( $post_id, $story_id ) {
    108172    init_wp_filesystem();
     
    123187    $story            = array();
    124188
    125     // Attempt to connect to the server
    126     $zip_file          = wp_tempnam( 'sh_zip', $tmpdir );
    127     $response          = sh_v2_api_request(
     189    // Attempt to connect to the server.
     190    $zip_file = wp_tempnam( 'sh_zip', $tmpdir );
     191    $response = shorthand_api_v2_request(
    128192        '/v2/stories/' . $story_id . ( $without_assets ? '?without_assets=true' : '' ) . ( $assets_only ? '?assets_only=true' : '' ),
    129193        array(
     
    145209        );
    146210    } else {
    147         $story = extractStoryContent( $zip_file, $destination_path, $story_id );
     211        $story = extract_story_content( $zip_file, $destination_path, $story_id );
    148212    }
    149213
     
    153217}
    154218
    155 
     219/**
     220 * Initializes the WordPress filesystem.
     221 *
     222 * Ensures that the WordPress filesystem is available for file operations.
     223 * If not, it requests filesystem credentials and re-initializes the filesystem.
     224 */
    156225function init_wp_filesystem() {
    157226    WP_Filesystem();
     
    163232}
    164233
    165 function extractStoryContent( $zip_file, $destination_path, $story_id ) {
     234/**
     235 * Extracts story content from a ZIP file.
     236 *
     237 * Extracts the 'head.html' and 'article.html' files from a given ZIP file
     238 * into a specified destination path.
     239 *
     240 * @param string $zip_file The path to the ZIP file.
     241 * @param string $destination_path The path to the extraction destination.
     242 * @param string $story_id The ID of the story.
     243 * @return array An array containing extracted content, path, or error information.
     244 */
     245function extract_story_content( $zip_file, $destination_path, $story_id ) {
    166246    // WP VIP HOSTING COMPATIBILITY
    167247    $zip = new ZipArchive();
  • shorthand-connect/trunk/includes/mass-pull.php

    r3095659 r3258349  
    11<?php
    2 /*
    3  * Sets each custom meta field, should be paired with sh_copy_story().
     2/**
     3 * Bulk story pull functions.
    44 *
     5 * @package Shorthand Connect
     6 */
     7
     8/**
     9 * Updates a story for a given post.
     10 *
     11 * Fetches story data from the shorthand API, copies story files, updates post
     12 * metadata, and performs post-processing on the story content.
    513 * Note: Unzipping a fresh story copy won't update the fields.
     14 *
     15 * @param int    $post_id The ID of the post.
     16 * @param string $story_id The ID of the story.
    617 */
    718function shand_update_story( $post_id, $story_id ) {
     
    1425
    1526    // Grab JSON for Story & Set into object $obj.
    16     $obj = sh_v2_api_request_json( '/v2/stories/' . $story_id . '/settings', array( 'timeout' => '240' ) );
     27    $obj = shorthand_api_request_json( '/v2/stories/' . $story_id . '/settings', array( 'timeout' => '240' ) );
    1728
    1829    $err = sh_copy_story( $post_id, $safe_story_id, $sh_media_cron_offload );
  • shorthand-connect/trunk/includes/shorthand-options.php

    r3095659 r3258349  
    11<?php
     2/**
     3 * Admin settings for the first time dialog.
     4 *
     5 * @package Shorthand Connect
     6 */
     7
    28/* Options */
    3 function shand_shorthand_menu() {
    4     add_options_page( 'Shorthand Options', 'Shorthand', 'manage_options', 'shorthand-options', 'shand_shorthand_options' );
    5 }
    6 $default_sh_site_css = '
    7 /* START CSS FOR DEFAULT WP THEMES */
    8 .site {
    9     margin: 0;
    10     max-width: none;
    11 }
    12 .site-content {
    13     padding: 0 !important;
    14 }
    15 .site-inner {
    16     max-width: none;
    17 }
    18 .site-header {
    19     max-width: none;
    20     z-index: 100;
    21 }
    22 .site:before {
    23     width: 0;
    24 }
    25 /* END CSS FOR DEFAULT WP THEMES */
    26 ';
    27 
    28 // JSON Checker
    29 function validate_json( $json_string ) {
    30     // Try to decode the JSON data. If it fails, the JSON is invalid.
    31     $json_data = json_decode( $json_string, true );
    32 
    33     if ( json_last_error() !== JSON_ERROR_NONE ) {
    34         // The JSON is invalid.
    35         return false;
    36     }
    37 
    38     // Return the original JSON string if it's valid.
    39     return $json_string;
    40 }
    41 
    42 function shand_shorthand_options() {
    43     global $default_sh_site_css;
    44     global $server_url;
    45 
    46     // Rather than running a rewrite flush everytime a post is submitted, run it on plugin activate/deactivate
    47     function shand_rewrite_flush() {
    48         shand_create_post_type();
    49         flush_rewrite_rules();
    50     }
    51     register_activation_hook( __FILE__, 'shand_rewrite_flush' );
    52     register_deactivation_hook( __FILE__, 'flush_rewrite_rules' );
     9
     10// Redirect to the first time setup.
     11function shorthand_redirect_admin_config() {
     12    $profile = shorthand_api_get_profile();
     13    if ( isset( $_GET['page'] ) && ( 'shorthand-options' === $_GET['page'] ) && ( ! $profile ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
     14        $admin_url = Shorthand_Admin::get_page_url( 'init' );
     15        wp_redirect( $admin_url );
     16        die();
     17    }
     18}
     19add_action( 'admin_menu', 'shorthand_redirect_admin_config' );
     20
     21function shorthand_shorthand_options() {
     22    // Menu links.
     23    $menu_links = array(
     24        'token'        => 'API Key',
     25        'permalink'    => 'Permalinks',
     26        'css'          => 'Custom CSS',
     27        'processing'   => 'Post-processing',
     28        'experimental' => 'Experimental Features',
     29    );
     30
     31    // If current = all, then display all.
     32    $current  = isset( $_GET['navigation'] ) ? sanitize_key( $_GET['navigation'] ) : array_keys( $menu_links )[0];
     33    $profile  = shorthand_api_get_profile();
     34    $messages = array();
    5335
    5436    if ( ! current_user_can( 'manage_options' ) ) {
    5537        wp_die( esc_html( __( 'You do not have sufficient permissions to access this page.' ) ) );
    5638    }
     39
    5740    if ( isset( $_POST['sh_submit_hidden'] ) && 'Y' === $_POST['sh_submit_hidden'] && check_admin_referer( 'sh-update-configuration' ) ) {
    5841        // If there's a token set, use it, if not set it to an empty string
    5942        $sh_v2_token = isset( $_POST['sh_v2_token'] ) ? sanitize_text_field( $_POST['sh_v2_token'] ) : '';
    6043        update_option( 'sh_v2_token', $sh_v2_token );
     44
     45        $profile = shorthand_api_get_profile();
     46        if ( $profile ) {
     47            $messages['updated'] = '<p>' . SHORTHAND_CONFIG_STEP1_SUCCESS . '</p><p><strong>Username</strong>: ' . esc_html( $profile->username ) . '</p>';
     48        } else {
     49            $messages['notice-error'] = SHORTHAND_CONFIG_STEP1_ERROR;
     50        }
    6151    }
    6252
     
    6454
    6555    if ( isset( $_POST['sh_submit_hidden_two'] ) && 'Y' === $_POST['sh_submit_hidden_two'] && check_admin_referer( 'sh-update-configuration' ) ) {
    66         // Check if there's custom CSS, if there is, use wp_kses_post() to sanitize otherwise set an empty string
     56        // Check if there's custom CSS, if there is, use wp_kses_post()
     57        // to sanitize otherwise set an empty string.
    6758        $sh_css = isset( $_POST['sh_css'] ) ? wp_kses_post( $_POST['sh_css'] ) : '';
    6859        update_option( 'sh_css', $sh_css );
     60        $messages['updated'] = SHORTHAND_CONFIG_STEP3_SUCCESS;
    6961    }
    7062
    7163    if ( isset( $_POST['sh_submit_hidden_three'] ) && 'Y' === $_POST['sh_submit_hidden_three'] && check_admin_referer( 'sh-update-configuration' ) ) {
    72         // Check if there's custom permalink, if there is, use sanitize_text_field() to sanitize potential HTML and then set an empty string
     64        // Check if there's custom permalink, if there is, use sanitize_text_field()
     65        // to sanitize potential HTML and then set an empty string.
    7366        $sh_permalink = isset( $_POST['sh_permalink'] ) ? sanitize_text_field( $_POST['sh_permalink'] ) : '';
    7467        update_option( 'sh_permalink', $sh_permalink );
    75         shand_rewrite_flush();
     68        shorthand_rewrite_flush();
     69        $messages['updated'] = SHORTHAND_CONFIG_STEP2_SUCCESS;
    7670    }
    7771    $permalink_structure = esc_html( get_option( 'sh_permalink' ) );
     
    8276    }
    8377    $sh_css = get_option( 'sh_css' );
    84     $no_css = false;
    85     if ( '' === $sh_css ) {
    86         $no_css = true;
    87         if ( isset( $default_site_css ) ) {
    88             update_option( 'sh_css', $default_site_css );
    89         }
    90         $sh_css = $default_sh_site_css;
    91     }
    92 
    93     if ( isset( $_POST['sh_submit_hidden_four'] ) && 'Y' === $_POST['sh_submit_hidden_four'] && check_admin_referer( 'sh-update-configuration' ) ) {
    94         $sh_regex_list = isset( $_POST['sh_regex_list'] ) ? wp_unslash( $_POST['sh_regex_list'] ) : '';
    95 
     78
     79    if ( isset( $_POST['sh_submit_hidden_four'] ) &&
     80        ( 'Y' === $_POST['sh_submit_hidden_four'] ) &&
     81        check_admin_referer( 'sh-update-configuration' )
     82    ) {
     83        // sh_regex_list may contain <tags> for lookup and processing on import and so may need to include <script> etc; however it is only ever displayed within a text-area value and manually processed.
     84        $sh_regex_list = empty( $_POST['sh_regex_list'] ) ?  '' : wp_unslash( $_POST['sh_regex_list'] ); //phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
     85
     86        $messages['updated'] = SHORTHAND_CONFIG_STEP4_SUCCESS;
    9687        if ( empty( $sh_regex_list ) ) {
    97             // Update the option with an empty value if the input is empty
     88            // Update the option with an empty value if the input is empty.
    9889            update_option( 'sh_regex_list', '' );
    9990        } else {
    10091            // Validate if it's a valid JSON without sanitizing
    101             $sh_regex_list = validate_json( $sh_regex_list );
     92            $sh_regex_list = Shorthand_Admin::validate_json( $sh_regex_list );
    10293
    10394            if ( false !== $sh_regex_list ) {
     
    10697            } else {
    10798                // Handle invalid JSON error here.
     99                unset( $messages['updated'] );
     100                $messages['notice-error'] = SHORTHAND_CONFIG_STEP4_ERROR;
    108101            }
    109102        }
     
    112105    $sh_regex_list = base64_decode( get_option( 'sh_regex_list' ) );
    113106
    114     // Experimental Settings
     107    // Experimental settings.
    115108    if ( isset( $_POST['sh_submit_hidden_experimental'] ) && 'Y' === $_POST['sh_submit_hidden_experimental'] && check_admin_referer( 'sh-update-configuration' ) ) {
    116109        $sh_media_cron_offload = isset( $_POST['sh_media_cron_offload'] ) ? filter_var( $_POST['sh_media_cron_offload'], FILTER_VALIDATE_BOOLEAN ) : false;
     
    118111        update_option( 'sh_media_cron_offload', $sh_media_cron_offload );
    119112        update_option( 'sh_disable_acf', $sh_disable_acf );
     113        $messages['updated'] = SHORTHAND_CONFIG_STEP5_SUCCESS;
    120114    }
    121115    $sh_media_cron_offload = filter_var( get_option( 'sh_media_cron_offload' ), FILTER_VALIDATE_BOOLEAN );
    122116    $sh_disable_acf        = filter_var( get_option( 'sh_disable_acf' ), FILTER_VALIDATE_BOOLEAN );
    123117
    124     $profile = sh_get_profile();
    125     ?>   
    126 <div class="container">
    127     <div class="py-1">
    128     <h1>Shorthand API Configuration</h1>
    129     <h2>Shorthand Connect Status</h2>
    130     <form name="formtoken" method="post">
    131     <?php wp_nonce_field( 'sh-update-configuration' ); ?>
    132         <input type="hidden" name="sh_submit_hidden" value="Y" />
    133         <table class="form-table"><tbody>
    134         <tr class="v2row">
    135         <th scope="row"><label for="sh_v2_token"><?php esc_html_e( 'Shorthand Team Token', 'sh-v2-token' ); ?></label></th>
    136         <td><input type="text" id="sh_v2_token" name="sh_v2_token" value="<?php echo esc_attr( $v2_token ); ?>" size="28"></td>
    137         </tr>
    138         </tbody></table>
    139     <?php if ( $profile ) { ?>
    140         <p class="status">Successfully connected</p>
    141         <p><strong>Username</strong>: <?php echo esc_html( $profile->username ); ?></p>
    142     <?php } else { ?>
    143         <p class="status warn">Not Connected</p>
    144     <?php } ?>
    145     <div style='clear:both'></div>
    146         <p class="submit">
    147         <input type="submit" name="Submit" class="button-primary" value="<?php esc_attr_e( 'Save Changes' ); ?>" />
     118    ?>
     119    <div class="wrap shorthand-options-wrapper">
     120    <h1>Settings</h1>
     121
     122    <hr />
     123    <div class="shorthand-menu">
     124        <ol role="list">
     125        <?php foreach ( $menu_links as $menu_key => $menu_name ) { ?>
     126        <li class="py-1">
     127        <form name="menu_settings_<?php echo esc_attr( $menu_key ); ?>" method="post" action="<?php echo esc_url( Shorthand_Admin::get_page_url( 'config', esc_attr( $menu_key ) ) ); ?>">
     128            <input type="submit" name="Submit" class="button<?php echo ( in_array( $current, array( $menu_key, 'all' ) ) ) ? ' button button-primary' : ''; ?>" value="<?php esc_attr_e( $menu_name ); ?>" />
     129        </form>
     130        </li>
     131        <?php } ?>
     132        </ol>
     133    </div>
     134
     135    <div class="shorthand-form-wrapper">
     136    <form name="form_settings" method="post" action="<?php echo esc_url( Shorthand_Admin::get_page_url( 'config', esc_attr( $current ) ) ); ?>">
     137
     138        <?php if ( ! empty( $messages ) ) : ?>
     139            <?php foreach ( $messages as $class => $message ) : ?>
     140            <div class="notice <?php echo esc_html ( $class ); ?>"><?php echo wp_kses( $message, array(
     141    'p' => array(),
     142    'strong' => array(),
     143) ); ?></div>
     144            <?php endforeach; ?>
     145        <?php endif; ?>
     146
     147        <?php if ( in_array( $current, array( array_keys( $menu_links )[0], 'all' ) ) ) : ?>
     148        <div class="py-1">
     149            <h2>Shorthand API Key</h2>
     150            <?php wp_nonce_field( 'sh-update-configuration' ); ?>
     151        <input type="hidden" name="sh_submit_hidden" value="Y"/>
     152        <p><label for="sh_v2_token"><?php esc_html_e( 'Your API key provided by Shorthand', 'shorthand-connect' ); ?></label></p>
     153        <input type="text" id="sh_v2_token" name="sh_v2_token" value="<?php echo esc_attr( $v2_token ); ?>">
     154        <p>
     155            <?php esc_html_e( 'Do not have API key?', 'shorthand-connect' ); ?> <a target="_blank" alt="(<?php esc_html_e( 'opens Shorthand Connect plugin settings', 'shorthand-connect' ); ?>)" href="https://support.shorthand.com/en/articles/62-programmatic-publishing-with-the-shorthand-api"><?php esc_html_e( 'Get one here', 'shorthand-connect' ); ?></a>
    148156        </p>
    149     </form>
    150     </div>
    151 
    152 
    153     <div class="py-1">
    154     <h2>Shorthand Permalink Structure</h2>
    155         <p>Use this to set the permalink structure of Shorthand story URLs</p>
    156         <form name="permalinks" method="post">
    157     <?php wp_nonce_field( 'sh-update-configuration' ); ?>
    158             <input type="hidden" name="sh_submit_hidden_three" value="Y" />
    159             <p>
    160                 <?php _e( 'Permalink structure:', 'sh-permalink-value' ); ?><br /><?php echo esc_url( get_site_url() ); ?>/<input type="text" name="sh_permalink" value="<?php echo esc_attr( $permalink_structure ); ?>" size="20">/{STORY_NAME}
    161             </p>
    162             <p class="submit">
    163                 <input type="submit" name="Submit" class="button-primary" value="<?php esc_attr_e( 'Save Changes' ); ?>" />
    164             </p>
    165         </form>
    166     </div>
    167 
    168     <div class="py-1">
    169     <h2>Shorthand Story Page CSS (theme wide CSS)</h2>
    170         <p>Use this CSS to customise Shorthand Story pages to better suit your theme</p>
    171     <?php if ( $no_css ) { ?>
    172             <p class="status warn">No custom CSS found, using default theme CSS</p>
    173     <?php } ?>
    174         <form name="themecss" method="post">
    175     <?php wp_nonce_field( 'sh-update-configuration' ); ?>
    176             <input type="hidden" name="sh_submit_hidden_two" value="Y" />
    177             <textarea rows="10" cols="80" name="sh_css"><?php echo esc_textarea( $sh_css ); ?></textarea>
    178             <p class="submit">
    179                 <input type="submit" name="Submit" class="button-primary" value="<?php esc_attr_e( 'Save Changes' ); ?>" />
    180             </p>
    181         </form>
    182         </div>
    183 
    184     <div class="py-1">   
    185     <h2>Post-processing</h2>
    186         <p>Use this to create a JSON object of regex queries and replacements.</p>
     157            <div style='clear:both'></div>
     158        </div>
     159        <?php endif; ?>
     160
     161        <?php if ( in_array( $current, array( array_keys( $menu_links )[1], 'all' ) ) ) : ?>
     162        <div class="py-1">
     163        <h2>Permalink Structure</h2>
     164            <?php wp_nonce_field( 'sh-update-configuration' ); ?>
     165        <input type="hidden" name="sh_submit_hidden_three" value="Y"/>
     166        <p>
     167        <p><label for="sh_permalink"><?php esc_html_e( 'Set the permalink structure of Shorthand story URLs', 'shorthand-connect' ); ?></label></p>
     168        <input type="text" name="sh_permalink" value="<?php echo esc_attr( $permalink_structure ); ?>" size="20">
     169        <p><?php echo esc_url( get_site_url() ); ?>/<strong><?php echo esc_html( $permalink_structure ); ?></strong>/{STORY_NAME}</p>
     170        </div>
     171        <?php endif; ?>
     172
     173        <?php if ( in_array( $current, array( array_keys( $menu_links )[2], 'all' ) ) ) : ?>
     174        <div class="py-1">
     175        <h2>Custom CSS</h2>
     176        <p>Use theme wide CSS to customise Shorthand Story pages to better suit your theme</p>
     177            <?php wp_nonce_field( 'sh-update-configuration' ); ?>
     178        <input type="hidden" name="sh_submit_hidden_two" value="Y"/>
     179        <textarea rows="10" cols="80" name="sh_css"><?php echo esc_textarea( $sh_css ); ?></textarea>
     180        </div>
     181        <?php endif; ?>
     182
     183        <?php if ( in_array( $current, array( array_keys( $menu_links )[3], 'all' ) ) ) : ?>
     184        <div class="py-1">
     185        <h2>Post-processing</h2>
     186        <p>Create a JSON object of regex queries and replacements.</p>
    187187        <p><em>This Example removes title tags from within the head tag by replacing it with nothing.</em></p>
    188 <pre><code>
     188        <pre><code class="post-processing">
    189189{
    190190    "head": [
    191191    {
    192         &quot;query&quot;: &quot;/&lt;title&gt;(.*?)&lt;\\/title&gt;/&quot;,
    193         &quot;replace&quot;: &quot;&quot;
     192    &quot;query&quot;: &quot;/&lt;title&gt;(.*?)&lt;\\/title&gt;/&quot;,
     193    &quot;replace&quot;: &quot;&quot;
    194194    }
    195195    ],
    196196    "body": []
    197197}
    198 
    199 </code></pre>
    200         <form name="postprocessing" method="post">
    201     <?php wp_nonce_field( 'sh-update-configuration' ); ?>
    202             <input type="hidden" name="sh_submit_hidden_four" value="Y" />
    203             <textarea rows="10" cols="80" id="sh_regex_list" name="sh_regex_list"><?php echo esc_textarea( $sh_regex_list ); ?></textarea>
    204             <p class="submit">
    205                 <input type="submit" name="Submit" class="button-primary" value="<?php esc_attr_e( 'Save Changes' ); ?>" />
    206             </p>
    207         </form>
     198        </code></pre>
     199            <?php wp_nonce_field( 'sh-update-configuration' ); ?>
     200        <input type="hidden" name="sh_submit_hidden_four" value="Y"/>
     201        <textarea rows="10" cols="80" id="sh_regex_list"
     202        name="sh_regex_list"><?php echo esc_textarea( $sh_regex_list ); ?></textarea>
    208203        <script>
    209204            let textarea = document.querySelector("textarea#sh_regex_list");
    210        
    211             textarea.addEventListener("keyup", function(event) {
    212                 try {
    213                     JSON.parse(textarea.value);
     205           
     206            textarea.addEventListener("keyup", function (event) {
     207            try {
     208            textarea.value = JSON.stringify(JSON.parse(textarea.value), undefined, 4);
     209            textarea.setCustomValidity("");
     210
     211            } catch (err) {
     212                if (textarea.value != "") {
     213                    console.log("Invalid JSON");
     214                    textarea.setCustomValidity("Invalid JSON in the Post-processing field");
     215                } else {
    214216                    textarea.setCustomValidity("");
    215                    
    216                 } catch(err) {
    217                     if(textarea.value != ""){
    218                         console.log("Invalid JSON");
    219                         textarea.setCustomValidity("Invalid JSON in the Post-processing field");
    220                     } else {
    221                         textarea.setCustomValidity("");
    222                     }
    223217                }
    224                
     218            }
     219
    225220            });
    226221        </script>
    227     </div>
    228 
    229     <div class="py-1">
    230     <h2>Experimental Features</h2>
    231     <p>Early access features that are still subject to change.</p>
    232     <form name="form_experimental" method="post">
    233     <?php wp_nonce_field( 'sh-update-configuration' ); ?>
     222        </div>
     223        <?php endif; ?>
     224
     225        <?php if ( in_array( $current, array( array_keys( $menu_links )[4], 'all' ) ) ) : ?>
     226        <div class="py-1">
     227        <h2>Experimental Features <span class="badge badge-blue">Advanced</span></h2>
     228        <p>Early access features that are still subject to change.</p>
     229            <?php wp_nonce_field( 'sh-update-configuration' ); ?>
    234230        <input type="hidden" name="sh_submit_hidden_experimental" value="Y"/>
    235         <input type="checkbox" id="sh_media_cron_offload" name="sh_media_cron_offload"
    236             value="true" <?php echo esc_attr( $sh_media_cron_offload ? 'checked' : '' ); ?> />
    237         <label for="sh_media_cron_offload">Import media assets via cron</label>
    238         <p>Assets will be fetched after story save to prevent potential execution timeouts. Media won't be immediately
    239         available on save but progress will be updated based on the `media_status` field.</p>
    240         <p>It is advised that Shorthand Story Posts are saved as a draft first to trigger the cron job prior to public
    241         publishing.</p>
     231
     232        <div class="checkbox-container">
     233        <input type="checkbox" id="sh_media_cron_offload" name="sh_media_cron_offload" <?php echo esc_attr( $sh_media_cron_offload ? 'checked' : '' ); ?> />
     234        <div class="bordered">
     235            <label for="sh_media_cron_offload"><strong>Import media assets via cron</strong>
     236            <p>Assets will be fetched after story save to prevent potential execution timeouts. Media won't be immediately
     237            available on save but progress will be updated based on the `media_status` field.</p>
     238            <p>It is advised that Shorthand Story Posts are saved as a draft first to trigger the cron job prior to public
     239            publishing.</p>
     240            </label>
     241        </div>
     242        </div>
     243
    242244        <br/>
     245        <div class="checkbox-container">
    243246        <input type="checkbox" id="sh_disable_acf" name="sh_disable_acf"
    244             value="true" <?php echo esc_attr( $sh_disable_acf ? 'checked' : '' ); ?> />
    245         <label for="sh_disable_acf">Disable Advanced Custom Fields</label>
    246         <p>Used to prevent any potential issues that could cause the Shorthand Custom Fields to become hidden by Advanced
    247         Custom Fields.</p>
     247        value="true" <?php echo esc_attr( $sh_disable_acf ? 'checked' : '' ); ?> />
     248        <div class="bordered"><label for="sh_disable_acf"><strong>Disable Advanced Custom Fields</strong>
     249            <p>Used to prevent any potential issues that could cause the Shorthand Custom Fields to become hidden by Advanced Custom Fields.</p></label></div>
     250        </div>
    248251        </br>
     252        </div>
     253        <?php endif; ?>
     254
     255        <div class="py-1">
     256        <hr />
    249257        <p class="submit">
    250         <input type="submit" name="Submit" class="button-primary" value="<?php esc_attr_e( 'Save Changes' ); ?>"/>
     258            <input type="submit" name="Submit" class="button button-primary" value="<?php esc_attr_e( 'Save changes' ); ?>"/>
    251259        </p>
     260        </div>
    252261    </form>
    253     </div>
    254 </div>
    255     </div>
    256 <style>
    257         .py-1 {
    258             padding: 1em;
    259         }
    260         .bg-white {
    261             background: white;
    262         }
    263         .container {
    264             max-width: 980px;
    265         }
    266         img.grav {
    267             float: left;
    268             width:80px;
    269             margin-right:10px;
    270         }
    271         p.status {
    272             background:#dfd;
    273             color:green;
    274             font-weight:bold;
    275             width:350px;
    276             clear:left;
    277             padding:5px;
    278         }
    279         p.status.warn {
    280             background:#ffd;
    281             color:orange;
    282         }
    283         .row-hidden {
    284             display:none;
    285         }
    286         #wpfooter {
    287             position: unset;
    288         }
    289         code {
    290             font-family: monospace;
    291             display: inherit;
    292         }
    293     </style>
    294 
     262
     263    </div>
     264    </div>
     265    </div>
    295266
    296267    <?php
    297268}
    298269
    299 add_action( 'admin_menu', 'shand_shorthand_menu' );
    300 
    301 ?>
     270function registerStyles() {
     271    // Adding styles.
     272    $css_path = '../css/options.css';
     273    wp_register_style( 'options_style', plugin_dir_url( __FILE__ ) . $css_path, array(), '1.3', 'all' );
     274    wp_enqueue_style( 'options_style' );
     275}
     276
     277add_action( 'init', 'registerStyles' );
     278
  • shorthand-connect/trunk/index.php

    r2898299 r3258349  
    1 <?php // Silence is golden
     1<?php
     2// Silence is golden.
  • shorthand-connect/trunk/shorthand-connect.php

    r3095659 r3258349  
    11<?php
    22/**
     3 * Main plugin file.
     4 *
    35 * @package Shorthand Connect
    4  * @version 1.3.31
    56 */
    67
     
    1011Description: Import your Shorthand stories into your WordPress CMS as simply as possible - magic!
    1112Author: Shorthand
    12 Version: 1.3.31
     13Version: 1.3.32
    1314Author URI: http://shorthand.com
    1415*/
    1516
    16 if ( file_exists( plugin_dir_path( __FILE__ ) . 'config.php' ) ) {
    17     include_once plugin_dir_path( __FILE__ ) . 'config.php';
     17// Make sure we don't expose any info if called directly.
     18if ( ! function_exists( 'add_action' ) ) {
     19    echo 'Hi there!  I\'m just a plugin, not much I can do when called directly.';
     20    exit;
     21}
     22
     23define( 'SHORTHAND_VERSION', '1.3.32' );
     24define( 'SHORTHAND__MINIMUM_WP_VERSION', '5.8' );
     25define( 'SHORTHAND__PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
     26
     27require_once SHORTHAND__PLUGIN_DIR . 'class-shorthand.php';
     28
     29// Add config.
     30if ( file_exists( SHORTHAND__PLUGIN_DIR . 'config.php' ) ) {
     31    include_once SHORTHAND__PLUGIN_DIR . 'config.php';
    1832} else {
    19     include_once plugin_dir_path( __FILE__ ) . 'config.default.php';
    20 }
    21 require_once plugin_dir_path( __FILE__ ) . 'includes/api.php';
    22 require_once plugin_dir_path( __FILE__ ) . 'includes/mass-pull.php';
    23 
    24 require_once plugin_dir_path( __FILE__ ) . 'includes/shorthand-options.php';
    25 require_once plugin_dir_path( __FILE__ ) . 'templates/abstract.php';
     33    include_once SHORTHAND__PLUGIN_DIR . 'config-default.php';
     34}
     35
     36require_once SHORTHAND__PLUGIN_DIR . 'includes/api.php';
     37require_once SHORTHAND__PLUGIN_DIR . 'includes/wp-api.php';
     38require_once SHORTHAND__PLUGIN_DIR . 'includes/mass-pull.php';
     39
     40if ( is_admin() || ( defined( 'WP_CLI' ) && WP_CLI ) ) {
     41    require_once SHORTHAND__PLUGIN_DIR . 'class-shorthand-admin.php';
     42    add_action( 'init', array( 'Shorthand_Admin', 'init' ) );
     43}
     44
     45// Loading initial setup or general configuration.
     46require_once SHORTHAND__PLUGIN_DIR . 'includes/shorthand-options-variables.php';
     47if ( isset( $_GET['view'] ) && ( $_GET['view'] === 'start' )) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended
     48    require_once SHORTHAND__PLUGIN_DIR . 'includes/shorthand-options-init.php';
     49} else {
     50    require_once SHORTHAND__PLUGIN_DIR . 'includes/shorthand-options.php';
     51}
     52
     53// Added admin menu.
     54function shorthand_shorthand_menu() {
     55    add_options_page( 'Shorthand Options', 'Shorthand', 'manage_options', 'shorthand-options', 'shorthand_shorthand_options' );
     56}
     57add_action( 'admin_menu', 'shorthand_shorthand_menu' );
     58
     59require_once SHORTHAND__PLUGIN_DIR . 'templates/abstract.php';
    2660
    2761if ( ! function_exists( 'WP_Filesystem' ) ) {
     
    73107add_action( 'init', 'shand_create_post_type' );
    74108
    75 function shand_wpt_shorthand_story() {
     109function shorthand_wpt_shorthand_story() {
    76110    global $post;
    77     global $server_url;
    78111    global $show_archived_stories;
    79112    $baseurl = '';
    80113
    81     ?>
    82     <style>
    83     li.story {
    84         float: left;
    85         width: 160px;
    86         margin: 5px;
    87     }
    88 
    89     li.story.selected label {
    90         background: #5b9dd9;
    91         color: #eee;
    92     }
    93 
    94     li.story input {
    95         display: none !important;
    96     }
    97 
    98     li.story label {
    99         display: block;
    100         background: #fafafa;
    101         text-align: center;
    102         height: 160px;
    103         padding-top: 5px;
    104         border: 1px solid #eeeeee;
    105     }
    106 
    107     li.story label:hover {
    108         border: 1px solid #5b9dd9;
    109     }
    110 
    111     li.story label span {
    112         display: block;
    113         font-weight: bold;
    114     }
    115 
    116     li.story label span.desc {
    117         display: none;
    118     }
    119 
    120     div.clear {
    121         clear: left;
    122     }
    123 
    124     div.publishing-actions {
    125         padding: 10px;
    126         clear: both;
    127         border-top: 1px solid #dcdcde;
    128         background: #f6f7f7;
    129         margin: -12px;
    130         margin-top: -12px;
    131         margin-top: 10px;
    132         display: flex;
    133         justify-content: center;
    134     }
    135 
    136     .button-shorthand {
    137         background-color: #000;
    138         border: 1px solid #000;
    139         transition: background-color .15s ease, color .15s ease;
    140         color: #fff;
    141         font-family: poppins, sans-serif;
    142         font-size: 13px;
    143         font-weight: 600;
    144         letter-spacing: 0;
    145         line-height: 1;
    146         padding: 1em 2em;
    147         text-decoration: none;
    148         cursor: pointer;
    149         border-radius: .9em;
    150     }
    151 
    152     .button-shorthand:hover {
    153         background-color: #fff;
    154         border-color: #000;
    155         color: #000;
    156     }
    157 
    158     .button-shorthand:disabled {
    159         pointer-events: none;
    160     }
    161 
    162     #codearea {
    163         border: 1px solid #999999;
    164         font-family: Consolas, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono, Bitstream Vera Sans Mono, Courier New, monospace;
    165         width: 100%;
    166         height: 300px;
    167     }
    168 
    169     #abstract {
    170         border: 1px solid #999999;
    171         width: 100%;
    172         height: 200px;
    173     }
    174 
    175     ul.stories {
    176         max-height: 400px;
    177         overflow-y: scroll;
    178     }
    179 
    180     ul.stories img {
    181         object-fit: cover;
    182         height: 110px;
    183     }
    184 
    185     p.warning {
    186         color: red;
    187         font-weight: bold;
    188     }
    189     </style>
    190     <?php
     114    // Adding styles.
     115    $css_path = 'css/connect.css';
     116    wp_register_style( 'connect_style', plugin_dir_url( __FILE__ ) . $css_path, array(), '1.3', 'all' );
     117    wp_enqueue_style( 'connect_style' );
    191118
    192119    $selected_story = get_post_meta( $post->ID, 'story_id', true );
     
    195122        return;
    196123    }
    197     $stories = sh_get_stories();
    198     $profile = sh_get_profile();
     124
     125    $profile = shorthand_api_get_profile();
    199126
    200127    if ( ! ( $profile ) ) {
    201         echo 'Could not connect to Shorthand, please check your API token in <a href="options-general.php?page=shorthand-options">Shorthand settings</a>.';
    202     } elseif ( $stories === null ) {
    203         echo 'You currently have no stories ready for publishing on Shorthand. Please check that your story is set to be ready for publishing.';
     128        $uri = Shorthand_Admin::get_page_url();
     129        printf( wp_kses( __( 'Could not connect to Shorthand, please check your API token in <a alt="(opens Shorthand Connect plugin settings)" href="%s">Shorthand settings</a>.' ) ), esc_url( $uri ) );
    204130    } else {
    205         echo '<ul class="stories">';
    206         foreach ( $stories as $story ) {
    207             $selected       = '';
    208             $story_selected = '';
    209             if ( $selected_story === $story->id ) {
    210                 $selected       = 'checked';
    211                 $story_selected = 'selected';
    212             }
    213             $archived = '';
    214             if ( isset( $story->story_version ) && '1' === $story->story_version ) {
    215                 if ( $show_archived_stories ) {
    216                     $archived = ' (archived)';
    217                 } else {
    218                     continue;
    219                 }
    220             }
    221             echo '<li class="story ' . esc_attr( $story_selected ) . '"><label><input name="story_id" type="radio" value="' . esc_attr( $story->id ) . '" ' . esc_html( $selected ) . ' /><img width="150" src="' . esc_url( $baseurl . $story->image ) . '" /><span class="title">' . esc_html( $story->title . $archived ) . '</span><span class="desc">' . esc_html( $story->metadata->description ) . '</span></a></label></li>';
    222         }
    223         echo '</ul><div class="clear"></div>';
     131        ?>
     132    <div id="stories-list">
     133    <div class="filter-container wp-core-ui">
     134        <input class="search" placeholder="Search for a story" />
     135        <span class="sort button-secondary desc" data-sort="updated_value">
     136        Edited<span class="sort-icon"></span>
     137        </span>
     138    </div>
     139    <!-- List to populate the stories. -->
     140    <ul class="list stories">
     141
     142    </ul>
     143    </div>
     144        <?php
     145        wp_register_script(
     146            'list-js',
     147            plugin_dir_url( __FILE__ ) . 'js/list.js/v2.3.4/list.min.js',
     148            array(),
     149            '2.3.4',
     150            array()
     151        );
     152        wp_enqueue_script( 'list-js' );
     153
     154        wp_register_script(
     155            'list-js-stories',
     156            plugin_dir_url( __FILE__ ) . 'js/connect-stories-list.js',
     157            array(),
     158            '1.0.0',
     159            array(
     160                'strategy' => 'defer',
     161            )
     162        );
     163        wp_enqueue_script( 'list-js-stories' );
     164
     165        wp_register_script(
     166            'list-js-stories-selected',
     167            plugin_dir_url( __FILE__ ) . 'js/connect-stories-list-selected.js',
     168            array(),
     169            '1.0.0',
     170            array(
     171                'strategy' => 'defer',
     172            )
     173        );
     174        wp_enqueue_script( 'list-js-stories-selected' );
     175        wp_register_script(
     176            'fetch-stories',
     177            plugin_dir_url( __FILE__ ) . 'js/connect-stories-fetch.js',
     178            array(),
     179            '1.0.0',
     180            array(
     181                'strategy' => 'defer',
     182            )
     183        );
     184        wp_localize_script('fetch-stories', 'wp_server', array(
     185            'url' => ( get_option('permalink_structure') ) ? "/wp-json/shorthand_connect/v1/stories/" : "/?rest_route=/shorthand_connect/v1/stories/",
     186            'nonce' => wp_create_nonce('wp_rest'),
     187            'selected_story' => $selected_story
     188        ));
     189        wp_enqueue_script( 'fetch-stories' );
     190        echo '<div class="clear"></div>';
    224191    }
    225192
    226193    // Noncename needed to verify where the data originated
    227     echo '<input type="hidden" name="eventmeta_noncename" id="eventmeta_noncename" value="' .
    228     esc_attr( wp_create_nonce( plugin_basename( __FILE__ ) ) ) . '" />';
    229 
     194    echo '<input type="hidden" name="eventmeta_noncename" id="eventmeta_noncename" value="' . esc_attr( wp_create_nonce( plugin_basename( __FILE__ ) ) ) . '" />';
     195}
     196
     197function shorthand_wpt_shorthand_story_advanced() {
    230198    ?>
     199    <div class="form-check form-switch text-light">
     200    <input class="form-check-input" type="checkbox" id="show-advanced">
     201    <label class="form-check-label" for="show-advanced">Show advanced options</label>
     202    </div>
    231203    <script>
    232     jQuery('li.story input:radio').click(function () {
    233         jQuery('li.story').removeClass('selected');
    234         jQuery(this).parent().parent().addClass('selected');
    235         jQuery('label#title-prompt-text').text('');
    236         jQuery('input#title').val(jQuery(this).parent().find('span.title').text());
    237         if (jQuery('textarea#abstract').val() === '') {
    238         jQuery('textarea#abstract').val(jQuery(this).parent().find('span.desc').text());
    239         }
    240     });
     204    //var elements = document.querySelectorAll( 'body *' );
     205    //document.getElementById("postcustom").style.visibility = 0;
     206    console.log(document.getElementById("postcustom"));
    241207    </script>
    242208    <?php
     
    259225        formaction="?shand_update"
    260226    />
    261 
    262     <script>
    263         jQuery('#post').submit(function () {
    264         jQuery('#shorthand_update').prop('disabled', true);
    265         });
    266     </script>
    267 
    268227    </div>
    269228
    270229    <?php
     230
     231    wp_register_script(
     232        'connect-form',
     233        plugin_dir_url( __FILE__ ) . 'js/connect-form.js',
     234        array(),
     235        '1.0.0',
     236        array(
     237            'strategy' => 'defer',
     238        )
     239    );
     240    wp_enqueue_script( 'connect-form' );
    271241}
    272242
     
    275245    global $noabstract;
    276246    $selected_story = get_post_meta( $post->ID, 'story_id', true );
     247
    277248    if ( $selected_story ) {
    278249        add_meta_box(
    279             'shand_wpt_shorthand_story_update',
     250            'shorthand_wpt_shorthand_story_update',
    280251            'Update Shorthand Story',
    281             'shand_wpt_shorthand_story',
     252            'shorthand_wpt_shorthand_story',
    282253            'shorthand_story',
    283254            'side',
     
    286257    } else {
    287258        add_meta_box(
    288             'shand_wpt_shorthand_story',
     259            'shorthand_wpt_shorthand_story',
    289260            'Select Shorthand Story',
    290             'shand_wpt_shorthand_story',
     261            'shorthand_wpt_shorthand_story',
    291262            'shorthand_story',
    292263            'normal',
    293             'default'
    294         );
    295     }
     264            'high'
     265        );
     266    }
     267
    296268    if ( ! $noabstract ) {
    297269        add_meta_box(
     
    300272            'shand_wpt_shorthand_abstract',
    301273            'shorthand_story',
    302             'normal',
    303             'default'
    304         );
    305     }
    306     add_meta_box( 'shand_wpt_shorthand_extra_html', 'Add additional HTML', 'shand_wpt_shorthand_extra_html', 'shorthand_story', 'normal', 'default' );
     274            'advanced'
     275        );
     276    }
     277
     278    add_meta_box(
     279        'shand_wpt_shorthand_extra_html',
     280        'Add additional HTML',
     281        'shand_wpt_shorthand_extra_html',
     282        'shorthand_story',
     283        'advanced'
     284    );
    307285}
    308286
     
    323301
    324302/* Save the shorthand story */
    325 function shand_save_shorthand_story( $post_id, $post ) {
     303function shorthand_save_shorthand_story( $post_id, $post ) {
     304    $profile = shorthand_api_get_profile();
     305    if ( ( get_post_type( $post ) === 'shorthand_story' ) && ! ( $profile ) ) {
     306        $uri = Shorthand_Admin::get_page_url();
     307        wp_die( message: sprintf( wp_kses_post ( __( 'Could not connect to Shorthand, please check your API token in <a alt="(opens Shorthand Connect plugin settings)" href="%s">Shorthand settings</a>.', 'shorthand-connect' ) ), esc_url( $uri ) ), title: wp_kses_post( __( 'Shorthand is not connected' ) ) );
     308    }
     309
    326310    WP_Filesystem();
    327311    global $wp_filesystem;
     
    363347        // Sanitize but also check if the query is GET or POST.
    364348        if ( isset( $_REQUEST['story_id'] ) ) {
    365             $story_id = filter_input( INPUT_GET, 'story_id', FILTER_SANITIZE_STRING );
    366             // If the variable is not present in the $_GET array.
    367             if ( null === $story_id ) {
    368                 $story_id = filter_input( INPUT_POST, 'story_id', FILTER_SANITIZE_STRING );
    369             }
     349            $story_id      =  sanitize_text_field( $_REQUEST['story_id'] );
    370350            $safe_story_id = preg_replace( '/\W|_/', '', $story_id );
    371351        }
     
    429409            if ( ! $noabstract ) {
    430410                $abstract = $body;
    431                 remove_action( 'save_post', 'shand_save_shorthand_story', 10, 3 );
     411                remove_action( 'save_post', 'shorthand_save_shorthand_story', 10, 3 );
    432412                $post = array(
    433413                    'ID'           => $post_id,
    434                     'post_content' => shand_abstract_template( $post_id, wp_kses_post( $_REQUEST['abstract'] ), $abstract ),
     414                    'post_content' => shorthand_abstract_template( $post_id, wp_kses_post( $_REQUEST['abstract'] ), $abstract ),
    435415                );
    436416                wp_update_post( $post );
    437                 add_action( 'save_post', 'shand_save_shorthand_story', 10, 3 );
     417                add_action( 'save_post', 'shorthand_save_shorthand_story', 10, 3 );
    438418            } else {
    439                 remove_action( 'save_post', 'shand_save_shorthand_story', 10, 3 );
     419                remove_action( 'save_post', 'shorthand_save_shorthand_story', 10, 3 );
    440420                $post = array(
    441421                    'ID'           => $post_id,
     
    443423                );
    444424                wp_update_post( $post );
    445                 add_action( 'save_post', 'shand_save_shorthand_story', 10, 3 );
     425                add_action( 'save_post', 'shorthand_save_shorthand_story', 10, 3 );
    446426            }
    447427
     
    461441}
    462442
    463 add_action( 'save_post', 'shand_save_shorthand_story', 10, 3 );
     443add_action( 'save_post', 'shorthand_save_shorthand_story', 10, 3 );
    464444
    465445/* Load Shorthand Template Hook */
     
    467447    global $post;
    468448    if ( 'shorthand_story' === $post->post_type ) {
    469         $path = locate_template( array( 'single-shorthand_story.php', 'templates/single-shorthand_story.php', 'template-parts/single-shorthand_story.php' ) );
     449        $path = locate_template(
     450            array(
     451                'single-shorthand_story.php',
     452                'templates/single-shorthand_story.php',
     453                'template-parts/single-shorthand_story.php',
     454                'single-shorthand-story.php',
     455                'templates/single-shorthand-story.php',
     456                'template-parts/single-shorthand-story.php',
     457            )
     458        );
    470459        if ( $path ) {
    471460            return $path;
    472461        }
    473462        $plugin_path   = plugin_dir_path( __FILE__ );
    474         $template_name = 'templates/single-shorthand_story.php';
     463        $template_name = 'templates/single-shorthand-story.php';
    475464        if ( ( get_stylesheet_directory() . '/' . $template_name === $template )
    476465            || ! file_exists( $plugin_path . $template_name )
     
    491480    if ( is_single() && 'shorthand_story' === get_post_type() ) {
    492481        $meta = get_post_meta( get_post()->ID );
    493         echo get_shorthandinfo( $meta, 'story_head' );
     482        echo ( get_shorthandinfo( $meta, 'story_head' ) );  // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
    494483    }
    495484}
     
    506495    if ( $query->is_main_query() && ! is_admin() ) {
    507496        $queried_object      = get_queried_object();
    508         $shorthand_templates = array( 'single-shorthand_story.php', 'templates/single-shorthand_story.php', 'template-parts/single-shorthand_story.php' );
     497        $shorthand_templates = array(
     498            'single-shorthand_story.php',
     499            'templates/single-shorthand_story.php',
     500            'template-parts/single-shorthand_story.php',
     501            'single-shorthand-story.php',
     502            'templates/single-shorthand-story.php',
     503            'template-parts/single-shorthand-story.php',
     504        );
    509505
    510506        // Check if the queried object uses a Shorthand Post template from the array.
     
    555551 * Activates plugin by creating post type.
    556552 */
    557 function shand_shorthand_activate() {
     553function shorthand_shorthand_activate() {
     554
    558555    shand_create_post_type();
    559     flush_rewrite_rules();
    560 }
    561 
    562 register_activation_hook( __FILE__, 'shand_shorthand_activate' );
     556    flush_rewrite_rules(); // phpcs:ignore WordPressVIPMinimum.Functions.RestrictedFunctions.flush_rewrite_rules_flush_rewrite_rules
     557
     558    // Set config values.
     559    update_option( 'sh_v2_token', '' );
     560    update_option( 'sh_permalink', 'shorthand_story' );
     561    $css_path = '/css/options-default.css';
     562    $sh_css   = file_get_contents( __DIR__. $css_path ); // phpcs:ignore WordPressVIPMinimum.Performance.FetchingRemoteData.FileGetContentsUnknown
     563    update_option( 'sh_css', $sh_css );
     564    update_option( 'sh_regex_list', '' );
     565    update_option( 'sh_media_cron_offload', true );
     566    update_option( 'sh_disable_acf', false );
     567}
     568
     569register_activation_hook( __FILE__, 'shorthand_shorthand_activate' );
    563570register_deactivation_hook( __FILE__, 'flush_rewrite_rules' );
    564571
  • shorthand-connect/trunk/templates/abstract.php

    r3095659 r3258349  
    11<?php
     2/**
     3 * Shorthand abstract template.
     4 *
     5 * @package Shorthand Connect
     6 */
    27
    3 function shand_abstract_template( $post_id, $abstract_html, $data ) {
    4     $abstract_html = '<p>' . $abstract_html . '</p>';
    5 
    6     $abstract_html .= '<a href="' . get_permalink( $post_id ) . '">View the story</a>';
    7 
    8     $abstract_html .= '<div style="display:none;">' . $data . '</div>';
    9     return $abstract_html;
     8/**
     9 * Generates a shorthand abstract template for a post.
     10 *
     11 * Creates a basic abstract template containing the provided abstract HTML,
     12a link to the post, and hidden data.
     13 *
     14 * @param int    $post_id The ID of the post.
     15 * @param string $abstract_html The HTML content for the abstract.
     16 * @param string $data Additional data to be included in a hidden div.
     17 * @return string The generated abstract HTML.
     18 */
     19function shorthand_abstract_template( $post_id, $abstract_html, $data ) {
     20    $title    = __( 'View the story' );
     21    $abstract = array(
     22        '<p>' . $abstract_html . '</p>',
     23        '<a href="' . get_permalink( $post_id ) . '">' . $title . '</a>',
     24        '<div style="display:none;">' . $data . '</div>',
     25    );
     26    return implode( $abstract );
    1027}
Note: See TracChangeset for help on using the changeset viewer.