Category Archives: Programming

WordPress Logo

Automatic Featured Image for WordPress Posts

The following script will automatically set the first image in a WordPress post as the Featured Image when the post is saved. You can override the functionality by selecting a Featured Image.

/**
 * @package WordPress
 * @subpackage Automatic Featured Image for WordPress Posts
 * @author That Stevens Guy
 * @phpcs:disable PSR1.Files.SideEffects
 */

/**
 * Transition post status action.
 *
 * @param string $new_status
 * @param string $old_status
 * @param WP_Post $post
 * @return void
 */
add_action('transition_post_status', function (string $new_status, string $old_status, WP_Post $post): void {
    if (defined('REST_REQUEST') && REST_REQUEST) {
        $published_post = $post;

        /**
         * REST requests need to postpone changes until "rest_after_insert_{$post->post_type}".
         *
         * @param WP_Post $post
         * @param WP_REST_Request $request
         * @param bool $creating
         * @return void
         */

        add_action("rest_after_insert_{$post->post_type}", function (
            WP_Post $post,
            WP_REST_Request $request,
            bool $creating
        ) use (
            $new_status,
            $old_status,
            $published_post
        ): void {
            if ($published_post->ID !== $post->ID) {
                return;
            }

            tsg_transition_post_status($new_status, $old_status, $post);
        }, 10, 3);
    } else {
        tsg_transition_post_status($new_status, $old_status, $post);
    }
}, 10, 3);

/**
 * Transition post status function.
 *
 * @param string $new_status
 * @param string $old_status
 * @param WP_Post $post
 * @return void
 */
function tsg_transition_post_status(string $new_status, string $old_status, WP_Post $post): void
{
    // Set the first image in post_content as the Featured Image. If one wasn't set.
    tsg_set_featured_image($post);
}

/**
 * Set the Featured Image automatically.
 *
 * @param WP_Post $post
 * @return void
 */
function tsg_set_featured_image(WP_Post $post): void
{
    if (!in_array($post->post_type, [ 'post' ])) {
        return;
    }

    // Bypass automatic featured image if the post thumbnail was set manually.
    if (has_post_thumbnail($post)) {
        return;
    }

    $attachment_ids = tsg_get_image_attachment_ids_from_post_content(
        $post,
        [
            'get_first_attachment_id' => true,
            'check_aspect_ratio' => true
        ]
    );

    if (!empty($attachment_ids[ 0 ])) {
        update_post_meta($post->ID, '_thumbnail_id', $attachment_ids[ 0 ]);
    }
}

/**
 * Get image attachment ids from post content.
 *
 * @param WP_Post $post
 * @param array $args
 * @return array
 */
function tsg_get_image_attachment_ids_from_post_content(WP_Post $post, array $args = []): array
{
    $args = array_merge([
        'get_first_attachment_id' => false,
        'check_aspect_ratio' => false,
        'horizontal_aspect_ratio' => 2.5,
        'vertical_aspect_ratio' => 2.5
    ], $args);

    $attachment_ids = [];

    $images = tsg_get_images_from_post_content($post);

    if (empty($images)) {
        return $attachment_ids;
    }

    $site_url = parse_url(site_url());

    foreach ($images as $image) {
        // If the image is NOT from the current site, skip it.
        if (strpos($image[ 'src' ], $site_url[ 'host' ] . '/' . explode('/', $image[ 'src' ])[ 3 ]) === false) {
            continue;
        }

        $guid = tsg_get_original_image_src($image[ 'src' ]);

        if (empty($guid)) {
            continue;
        }

        $attachment_id = tsg_get_post_id_by_guid($guid);

        if (empty($attachment_id)) {
            continue;
        }

        if ($args[ 'check_aspect_ratio' ]) {
            $attachment_metadata = get_metadata('post', $attachment_id, '_wp_attachment_metadata', true);

            if (
                !tsg_check_image_aspect_ratio(
                    $attachment_metadata,
                    $args[ 'horizontal_aspect_ratio' ],
                    $args[ 'vertical_aspect_ratio' ]
                )
            ) {
                continue;
            }
        }

        $attachment_ids[] = $attachment_id;

        if ($args[ 'get_first_attachment_id' ]) {
            break;
        }
    }

    return $attachment_ids;
}

/**
 * Get the original image source, size 'full'.
 *
 * @param string $url
 * @param array $args
 * @return string
 */
function tsg_get_original_image_src(string $url, array $args = []): string
{
    if (!$url) {
        return $url;
    }

    $args = array_merge([
        'check_exists' => false,
        'check_filesize' => false,
        'filesize_limit' => 4000000,
        'strip_edit' => false
    ], $args);

    // Strip the thumbnail size at the end of the URL so that we end up with what
    // potentially could be the full size original image source.
    //
    // There is an edge case where the original URL has dimensions in the filename
    // with the same format. These will be skipped, this is unaviodable, particularly
    // for an offsite URL.
    $url = preg_replace("/\-\d{2,4}[xX]\d{2,4}(\.[a-zA-Z]{2,4})$/", '$1', $url);

    // Strip the edit timestamp for WordPress edited images.
    // Turns out this isn't the best idea, end up with unedited images. But can be used for some things.
    if (!empty($args[ 'strip_edit' ])) {
        if (strpos($url, '-e') !== false) {
            $pathinfo = pathinfo($url);

            if (
                !empty($pathinfo[ 'dirname' ]) &&
                !empty($pathinfo[ 'filename' ]) &&
                !empty($pathinfo[ 'extension' ])
            ) {
                $filename_split = array_reverse(explode('-e', $pathinfo[ 'filename' ]));

                if (!empty($filename_split[ 0 ]) && is_int((int)$filename_split[ 0 ])) {
                    unset($filename_split[ 0 ]);
                }

                $url = $pathinfo[ 'dirname' ] . '/' .
                    implode('-e', array_reverse($filename_split)) . '.' . $pathinfo [ 'extension' ];
            }
        }
    }

    // Because we've chopped the URL up so much, we may want to check if the image even exists.
    if (!empty($args[ 'check_exists' ]) || !empty($args[ 'check_filesize' ])) {
        $stream_options = [
            'http' => [
                'user_agent' =>
                    "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.6) Gecko/20070725 Firefox/2.0.0.6"
            ],
            // 'ssl'  => [
            //     'verify_peer' => false,
            //     'verify_peer_name' => false
            // ]
        ];
        $stream_context = stream_context_create($stream_options);
        $headers = @get_headers($url, true, $stream_context);

        if (!empty($args[ 'check_exists' ])) {
            if (empty($headers[ 0 ]) || strpos($headers[ 0 ], '404') !== false) {
                $url = '';
            }
        }

        if ($url && !empty($args[ 'check_filesize' ]) && !empty($args[ 'filesize_limit' ])) {
            if (
                empty($headers[ 'Content-Length' ]) ||
                (int)$headers[ 'Content-Length' ] > (int)$args[ 'filesize_limit' ]
            ) {
                $url = '';
            }
        }
    }

    return $url;
}

/**
 * Get all images from the post content.
 *
 * @param WP_Post $post
 * @return array
 */
function tsg_get_images_from_post_content(WP_Post $post): array
{
    $images = [];

    if (empty($post->post_content)) {
        return $images;
    }

    $content = apply_filters('the_content', $post->post_content);

    preg_match_all('/<img\b[^>]+src=[\'"]([^\'"]+\.(?:jpg|png|jpeg))[\'"][^>]*>/i', $content, $matchesImages);

    if (!empty($matchesImages[ 0 ])) {
        foreach ($matchesImages[ 0 ] as $key => $img) {
            $images[ $key ][ 'img' ] = $img;
            $images[ $key ][ 'src' ] = $matchesImages[ 1 ][ $key ];

            preg_match_all(
                '/(<img\b|(?!^)\G)[^>]*?\b(alt|width|height|srcset|sizes)=([\'"]?)([^>]*?)\3/i',
                $img,
                $matchesAttr
            );

            if (!empty($matchesAttr[ 2 ])) {
                foreach ($matchesAttr[ 2 ] as $attr_key => $attr) {
                    if (!empty($matchesAttr[ 4 ][ $attr_key ])) {
                        $images[ $key ][ $attr ] = $matchesAttr[ 4 ][ $attr_key ];
                    }
                }
            }
        }
    }

    $images = apply_filters('tsg_get_images_from_post_content', $images, $post);

    return $images;
}

/**
 * Get check if an image fits within a suitable aspect ratio.
 *
 * @param array $image [ 'height' => int, 'width' => int ]
 * @param float $horizontal_aspect_ratio
 * @param float $vertical_aspect_ratio
 * @return bool
 */
function tsg_check_image_aspect_ratio(
    array $image,
    float $horizontal_aspect_ratio = 2.5,
    float $vertical_aspect_ratio = 2.5
): bool {
    if (empty($image[ 'width' ]) || empty($image[ 'height' ])) {
        return false;
    }

    $calculated_horizontal_aspect_ratio = (int)$image[ 'width' ] / (int)$image[ 'height' ];
    $calculated_vertical_aspect_ratio = (int)$image[ 'height' ] / (int)$image[ 'width' ];

    if (
        $calculated_horizontal_aspect_ratio > $horizontal_aspect_ratio ||
        $calculated_vertical_aspect_ratio > $vertical_aspect_ratio
    ) {
        return false;
    }

    return true;
}

/**
 * Get post ID by guid.
 *
 * @param string $guid
 * @return int ID if found, 0 if not
 */
function tsg_get_post_id_by_guid(string $guid): int
{
    global $wpdb;

    $post_id = $wpdb->get_var(
        $wpdb->prepare("
            SELECT ID
            FROM $wpdb->posts
            WHERE instr( guid, '%s' ) > 0
        ", $guid)
    );

    return intval($post_id);
}

https://gist.github.com/ThatStevensGuy/7020010fe667106f79d2556f386933d0

Windows 10 Logo

How to Reset Your Windows 10 Password

I have pieced together several guides to come up with a reliable method of resetting a Windows 7, 8 or 10 password. I’m not sure if it will work for user accounts tied to a Windows Live account on Windows 10 though. The below video by Nehal J Wani on YouTube forms the core of this solution. You should only need to use this method if you do not have a copy of Windows, or a Password Recovery Disk.

Download UNetbootin. This application creates bootable Linux Distributions. It is cross platform but my guide will focus on creating the Live USB using Mac. Bring up the context menu in Finder and select Open. Enter your password to let Mac open the application.

Unfortunately the Distribution is not listed as a download in this application. So you’ll want to download SystemRescueCd ISO as suggested by the YouTube video. It should be easy enough for you to use UNetbootin to create a bootable USB from the downloaded ISO. If you are unsure which drive is your USB, enter diskutil list in the Terminal.

Reboot the problem computer and boot from the USB. Some BIOS will complain about the USB breaching a security policy. You’ll need to enter BIOS and disable the UEFI security. This will vary based on your BIOS version. It is usually pretty obvious and listed under Boot or Security.

Once you are able to boot from the USB. Select the first option and follow the prompts until you reach the Terminal. Type gdisk -l /dev/sda to find your Windows partition. The largest partition on your main drive is usually the safe bet.

You can try to mount the Windows partition using mount /dev/sda? /mnt (replace ? with the partition number). If you get an error message about the NTFS partition being hibernated you can try mount -t ntfs-3g -o remove_hiberfile /dev/sda? /mnt as an alternative.

Now we can perform the steps as demonstrated in the video:

  1. cd /mnt/Windows/System32/Config
  2. chntpw -i SAM
  3. Press 1
  4. Enter the RID of the user you wish to change the password for.
  5. Press 1
  6. Press q
  7. Press q
  8. Press y
  9. cd
  10. umount /mnt
  11. reboot

While the computer reboots, unplug the USB. Your user account should now login without any need for a password. Once logged in, you can go into your user account and reset your password from within Windows.

There are several other methods of resetting a Windows password. It really comes down to what you have available. I believe the Offline Windows Password & Registry Editor uses a very similar process. There is also the Sticky Keys trick, but that only works if Sticky Keys are enabled prior to being locked out.

jQuery Logo

jQuery Smooth Scroll to Anchor

Have you ever wanted to smoothly scroll to an anchor on a page with jQuery? The script example I have provided allows several functionalities. If you enter a page with an anchor hash directly, it will smooth scroll to that point on the page. It will also update the hash, and calculate the height of a sticky header or menu.

This works best as an inline script, but does also work when placed inside a JavaScript file.

/**
 * jQuery Smooth Scroll to Anchor
 */
var pauseHashOnChange = false;

// Initialises the Smooth Scroll Functionality
function initSmoothScroll( e, ele ) 
{    
    e.preventDefault();
    url = jQuery( ele ).prop('href');
    hash = url.substring( url.indexOf('#') + 1 );
    hashChange( hash );
}

// Hash Change
function hashChange( hash )
{
    hash = hash ? hash : window.location.hash.slice(1);
    ele = 'a[name=' + hash + ']';

    if ( hash && jQuery( ele ).hasClass('smooth-scroll-target') )
    {
        jQuery( ele ).trigger('click');
        smoothScroll( ele );
    }    
}

// Smooth Scroll
function smoothScroll( ele )
{
    ele = jQuery(ele);
    extraOffset = 30;
    headerOffset = jQuery('header').height();
    offset = headerOffset - extraOffset;

    jQuery('html, body').stop().animate({
        'scrollTop' : ele.offset().top - offset
    }, 900, 'swing', function() {
        pauseHashOnChange = true;
        window.location.hash = ele.prop('name');
        pauseHashOnChange = false;
    });
}

// Run Hash Change onload to trigger the click event and then scroll to the anchor.
jQuery(window).load( function() {
    hashChange();
});

jQuery(document).ready( function($) {
    
    // Listen for Hash Change events.
    $(window).on('hashchange', function() { 
        if ( !pauseHashOnChange )
            hashChange();
    });
    
    // Attach the Smooth Scroll event to a class.
    $('.smooth-scroll').click( function( e ) {
        initSmoothScroll( e, this );
    });
});
<a href="#anchor" class="smooth-scroll">Smooth Scroll</a>
<a name="anchor" class="smooth-scroll-target"></a>