Skip to content

Instantly share code, notes, and snippets.

@brichards
Last active February 16, 2021 17:10
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save brichards/5020710 to your computer and use it in GitHub Desktop.
Save brichards/5020710 to your computer and use it in GitHub Desktop.
StartBox GitHub Download Generator
<?php
/**
* Plugin Name: StartBox GitHub .zip Generator
* Description: Generates a .zip download for StartBox from GitHub Master.
*
* @props Konstantin Kovshenin (@kovshenin) for sharing his work used on underscores.me (http://code.svn.wordpress.org/underscoresme/plugins/underscoresme-generator/underscoresme-generator.php)
*/
class SB_Download_Generator {
/**
* Setup all our variables and hooks
*/
function __construct() {
// Setup our variables
$this->github_repo = 'https://api.github.com/repos/WebDevStudios/StartBox'; // The repo we're harvesting
$this->last_commit_date = ( $stored = get_option( 'github_last_commit_date' ) ) ? $stored : date( 'c', strtotime('-1 year') ); // Falls back to 1yr ago
$this->pagename = 'download'; // The page slug we're hijacking
$this->filename = 'startbox.zip'; // Our desired filename
$this->tmp_dir = ABSPATH . 'tmp/'; // The temp directory where we'll store everything
$this->tmp_file = $this->tmp_dir . 'sb.zip'; // The temp zip we'll create when downloading from github
$this->download_file = $this->tmp_dir . $this->filename; // The download file we'll serve to everyone
$this->excluded_files = array( '.git', '.svn', '.DS_Store', '.gitignore', '.', '..' ); // Optional files to exclude in the served zip
$this->excluded_dirs = array( '.git', '.svn', '.', '..' ); // Optional directories to exclude in the served zip
// All the black magic is happening here
add_action( 'parse_request', array( $this, 'init' ) );
}
/**
* Listens for a download request and fires things up
*/
function init( &$wp ) {
// Listen for someone trying to access our target pagename
if ( $this->pagename == $wp->query_vars['pagename'] ) {
// If github has a newer commit, or we don't have a download file prepped, let's make a new zip
if ( $this->is_github_newer() || ! file_exists( $this->download_file ) ) {
$this->remote_get();
$this->make_archive();
}
$this->serve_download();
}
}
/**
* Check if github has a more recent commit
*
* @return boolean True if github's commit is newer, false if current
*/
function is_github_newer() {
// Grab the newest commit date
$remote_request = wp_remote_get( $this->github_repo . '/commits?sha=master&since=' . $this->last_commit_date );
$request_body = json_decode($remote_request['body'], true );
$current_commit = ( is_array( $request_body ) && !empty( $request_body ) ) ? $request_body[0]['commit']['author']['date'] : $this->last_commit_date;
// If current commit is newer, update option and return true
if ( strtotime( $current_commit ) > strtotime( $this->last_commit_date ) ) {
update_option( 'github_last_commit_date', $current_commit );
return true;
}
// Otherwise, we're current
return false;
}
/**
* Grabs our zipball from github and unpacks it locally
*/
function remote_get() {
// Fetch our file from github and save it locallaly
$remote_file = wp_remote_get( $this->github_repo . '/zipball' );
$fp = fopen( $this->tmp_file, 'w' );
fwrite( $fp, $remote_file['body'] );
fclose( $fp );
// Fire up ZipArchive
$zip = new ZipArchive;
$res = $zip->open( $this->tmp_file, ZipArchive::CREATE && ZipArchive::OVERWRITE );
// Unzip the contents and write them to an array
$zip->extractTo( $this->tmp_dir . 'startbox/' );
$extracted_contents = scandir( $this->tmp_dir . 'startbox/', 1 );
$this->extracted_dir = trailingslashit( $this->tmp_dir . 'startbox/' . $extracted_contents[0] );
// Close and delete our zip
$zip->close();
unlink( $this->tmp_file );
}
/**
* Creates a fresh archive from the unpacked files
*/
function make_archive() {
// Fire up ZipArchive
$zip = new ZipArchive;
$res = $zip->open( $this->download_file, ZipArchive::CREATE && ZipArchive::OVERWRITE );
// Loop through our extracted contents and add anything we havent excluded
$iterator = new RecursiveDirectoryIterator( $this->extracted_dir );
foreach ( new RecursiveIteratorIterator( $iterator ) as $filename ) {
if ( in_array( basename( $filename ), $this->excluded_files ) )
continue;
foreach ( $this->excluded_dirs as $directory )
if ( strstr( $filename, "/{$directory}/" ) )
continue 2; // continue the parent foreach loop
$local_filename = str_replace( trailingslashit( $this->extracted_dir ), '', $filename );
$contents = file_get_contents( $filename );
$zip->addFromString( $local_filename, $contents );
}
// Close our zip
$zip->close();
// Delete our temporory dir
// $this->remove_dir( $this->extracted_dir );
}
/**
* Serves up a download for our zip
*/
function serve_download() {
header( 'Content-type: application/zip' );
header( sprintf( 'Content-Disposition: attachment; filename="%s"', $this->filename ) );
readfile( $this->download_file );
die();
}
/**
* Recursively loops through a directory and deletes it plus all contents.
*
* @param string $dir The directory to delete
*/
function remove_dir( $dir ) {
if ( is_dir($dir) ) {
$objects = scandir( $dir );
foreach ( $objects as $object ) {
if ( '.' != $object && '..' != $object ) {
if ( 'dir' == filetype( $dir . '/' . $object ) )
$this->remove_dir( $dir . '/' . $object );
else
unlink( $dir . '/' . $object );
}
}
reset( $objects );
rmdir( $dir );
}
}
}
new SB_Download_Generator;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment