Last active
September 21, 2018 20:45
-
-
Save earth3300/3f9ed7ae03ee715d4b1bfe1b12a16075 to your computer and use it in GitHub Desktop.
A single file that lists the media files in a given directory. Can also be used as a WordPress plugin.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| /** | |
| * EC01 Media Index. | |
| * | |
| * Allows media (images, video and audio) to be viewed in a directory through a | |
| * single index file. | |
| * | |
| * @package EC01 Media Index | |
| * @since 1.0.0 | |
| * @author Clarence Bos <cbos@tnoep.ca> | |
| * @copyright Copyright (c) 2018, Clarence Bos | |
| * @license https://www.gnu.org/licenses/gpl-3.0.en.html GPL-3.0+ | |
| * @link http://wp.cbos.ca/plugins/ec01-media-index | |
| * @see https://carlalexander.ca/designing-class-generate-wordpress-html-content/ | |
| * | |
| * @wordpress-plugin | |
| * Plugin Name: EC01 Media Index | |
| * Plugin URI: http://wp.cbos.ca/plugins/ec01-media-index/ | |
| * Description: Allows media (image, video, audio) to be viewed in a directory through a single file. Shortcode [media-list]. | |
| * Version: 1.0.0 | |
| * Author: Clarence Bos | |
| * Author URI: http://ec01.earth3300.info/ | |
| * Text Domain: ec01-media-list | |
| * License: GPL-3.0+ | |
| * License URI: https://www.gnu.org/licenses/gpl-3.0.en.html | |
| */ | |
| /** | |
| * Allows media (jpg, png, mp3, mp4) to be viewed in the given directory. | |
| * | |
| * See the bottom of this file for a more complete description | |
| * and the switch for determining the context in which this file | |
| * is found. | |
| */ | |
| class MediaList | |
| { | |
| /** @var array Default options. */ | |
| protected $opts = [ | |
| 'dim' => [ 'width' => 800, 'height' => 600 ], | |
| 'max' => 12, | |
| 'msg' => [ 'na' => '', ], | |
| 'subtypes' => [ | |
| 'jpg' => [ 'type' => 'image' ], | |
| 'png'=> [ 'type' => 'image' ], | |
| 'mp4' => [ 'type' => 'video' ], | |
| 'mp3' => [ 'type' => 'audio' ], | |
| ], | |
| ]; | |
| /** | |
| * Get the list of images as HTML. | |
| * | |
| * In order to process the media file correctly, the browser has to know what | |
| * MIME (Multipurpose Internet Mail Extension) it is. The MIME type has the | |
| * format: `type/subtype`, thus `image/png`, `image/jpeg`, `video/mp4` or | |
| * `audio/mpeg` {@link https://tools.ietf.org/html/rfc3003}. We are simply | |
| * using the extensions (mp3, mp4, jpg and png) here and requiring the person | |
| * responsible for the content to ensure consistency and that the extension | |
| * correlates with the MIME type for that file (i.e mp3 _is_ an audio file | |
| * in the mpeg format). The `jpeg` MIME type is expected to be found as `jpg` | |
| * NOT `jpeg`, again for programmatic and visual consistency. | |
| * | |
| * @link https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types | |
| * | |
| * @param array $args | |
| * | |
| * @return string | |
| */ | |
| public function get( $args ) | |
| { | |
| if ( $this->checkArgs( $args ) ) | |
| { | |
| $max = $this->getMaxImages( $args ); | |
| $args['self'] = $this->isDirSelf( $args ); | |
| $subtypes = $this->opts['subtypes']; | |
| $str = '<article>' . PHP_EOL; | |
| foreach ( $subtypes as $subtype => $type ) { | |
| $args['type'] = $type['type']; | |
| $args['subtype'] = $subtype; | |
| if ( $match = $this->getMatchPattern( $subtype, $args ) ) | |
| { | |
| $str .= $this->iterateFiles( $match, $max, $args ); | |
| } | |
| } | |
| $str .= '</article>' . PHP_EOL; | |
| if ( isset( $args['doctype'] ) && $args['doctype'] ) | |
| { | |
| $str = $this->getPageHtml( $str, $args ); | |
| } | |
| return $str; | |
| } | |
| else | |
| { | |
| return "Error."; | |
| } | |
| } | |
| /** | |
| * Iterate over files. | |
| * | |
| * Capability for jpg, png, mp3 and mp4 | |
| * | |
| * @param string $match | |
| * @param array $args | |
| * | |
| * @return string | |
| */ | |
| private function iterateFiles( $match, $max, $args ) | |
| { | |
| $str = ''; | |
| $cnt = 0; | |
| foreach ( glob( $match ) as $file ) | |
| { | |
| $cnt++; | |
| if ( $cnt > $max ) | |
| { | |
| break; | |
| } | |
| $args['file'] = $file; | |
| /** Remove the root of the file path to use it an image source. */ | |
| $args['src'] = $this->getSrcFromFile( $args['file'] ); | |
| $args['name-dim'] = $this->getImageNameDimArr( $args['src'] ); | |
| $args['name'] = $args['name-dim']['name']; | |
| $args['dim'] = $this->getMediaDimArr( $args['name-dim']['strDim'] ); | |
| $str .= $this->getMediaItemHtml( $args ); | |
| } | |
| return $str; | |
| } | |
| /** | |
| * Get the source from the file, checking for a preceding slash. | |
| * | |
| * @param string $str | |
| * @return string | |
| */ | |
| private function getSrcFromFile( $str ) | |
| { | |
| $src = str_replace( $this->getSitePath(), '', $str ); | |
| /** May be server inconsistency, therefore remove and add again. */ | |
| $src = ltrim( $src, '/' ); | |
| return '/' . $src; | |
| } | |
| /** | |
| * Get the SITE_PATH | |
| * | |
| * Get the SITE_PATH from the constant, from ABSPATH (if loading within WordPress | |
| * as a plugin), else from the $_SERVER['DOCUMENT_ROOT'] | |
| * | |
| * Both of these have been tested online to have a preceding forward slash. | |
| * Therefore do not add one later. | |
| * | |
| * @return bool | |
| */ | |
| private function getSitePath() | |
| { | |
| if ( defined( 'SITE_PATH' ) ) | |
| { | |
| return SITE_PATH; | |
| } | |
| /** Available if loading within WordPress as a plugin. */ | |
| elseif( defined( 'ABSPATH' ) ) | |
| { | |
| return ABSPATH; | |
| } | |
| else | |
| { | |
| return $_SERVER['DOCUMENT_ROOT']; | |
| } | |
| } | |
| /** | |
| * Get the "Self" directory, if set. | |
| * | |
| * @param array $args | |
| * | |
| * @return bool | |
| */ | |
| private function isDirSelf( $args ) | |
| { | |
| if ( isset( $args['self'] ) && true == $args['self'] ) | |
| { | |
| return true; | |
| } | |
| else | |
| { | |
| return false; | |
| } | |
| } | |
| /** | |
| * Get the maximum number of images to process. | |
| * | |
| * @param array $args | |
| * @return int | |
| */ | |
| private function getMaxImages( $args ) | |
| { | |
| if ( isset( $args['max'] ) ) | |
| { | |
| $max = $args['max']; | |
| } | |
| else | |
| { | |
| $max = $this->opts['max']; | |
| } | |
| return $max; | |
| } | |
| /** | |
| * Build the match string. | |
| * | |
| * This is iterated through for each type added to $types, above. A basic | |
| * check for a reasonable string length (currently 10) is in place. Can | |
| * develop this further, if needed. | |
| * | |
| * @param string $type 'jpg', 'png' | |
| * @param array $args | |
| * | |
| * @return string|false | |
| */ | |
| private function getMatchPattern( $type, $args ) | |
| { | |
| $path = $this->getBasePath( $args ); | |
| $prefix = "/*"; | |
| $match = $path . $prefix . $type; | |
| /** Very basic check. Can improve, if needed. */ | |
| if ( strlen( $match ) > 10 ) | |
| { | |
| return $match; | |
| } | |
| else { | |
| return false; | |
| } | |
| } | |
| /** | |
| * Get the Base Path to the Media Directory. | |
| * | |
| * This does not need to include the `/media` directory. | |
| * | |
| * @param array $args | |
| * @return string | |
| */ | |
| private function getBasePath( $args ) | |
| { | |
| if ( isset( $args['self'] ) ) | |
| { | |
| $path = __DIR__; | |
| } | |
| elseif ( defined( 'SITE_CDN_PATH' ) ) | |
| { | |
| $path = SITE_CDN_PATH; | |
| } | |
| return $path; | |
| } | |
| /** | |
| * Get the Media Directory | |
| * | |
| * @param array $args | |
| * | |
| * @return string | |
| * | |
| * @example $args['dir'] = '/architecture/shelter/micro-cabin/' | |
| */ | |
| private function getMediaDir( $args ) | |
| { | |
| if ( isset( $args['dir'] ) ) | |
| { | |
| $media = $args['dir']; | |
| } | |
| else | |
| { | |
| $media = '/media'; | |
| } | |
| return $media; | |
| } | |
| /** | |
| * Get the Media Item HTML. | |
| * | |
| * If no type is specified, return the default (.jpg). Include the dot (.) | |
| * for simplicity. | |
| * | |
| * jpg, png, mp3, mp4 (Default: jpg) | |
| * | |
| * @param array $args | |
| * | |
| * @return string | |
| */ | |
| private function getMediaItemHtml( $args ) | |
| { | |
| $str = ''; | |
| switch( $args['type'] ) | |
| { | |
| case 'image': | |
| $str = $this-> getImageHtml( $args); | |
| break; | |
| case 'audio': | |
| $str = $this-> getAudioHtml( $args); | |
| break; | |
| case 'video': | |
| $str = $this-> getVideoHtml( $args); | |
| break; | |
| default: | |
| $str = $this-> getImageHtml( $args); | |
| break; | |
| } | |
| return $str; | |
| } | |
| /** | |
| * Get the Image HTML. | |
| * | |
| * Types: jpg, png | |
| * | |
| * @param array $args | |
| * | |
| * @return string | |
| */ | |
| private function getImageHtml( $args ) | |
| { | |
| $str = '<div class="media image">' . PHP_EOL; | |
| $str .= sprintf( '<a href="%s">%s', $args['src'], PHP_EOL ); | |
| $str .= '<img'; | |
| $str .= sprintf( ' class="%s"', $this->getImageClass( $args ) ); | |
| $str .= sprintf( ' src="%s"', $args['src'] ); | |
| $str .= sprintf( ' alt="%s"', $this->getImageAlt( $args ) ); | |
| $str .= sprintf( ' width="%s"', $this->getImageWidth( $args ) ); | |
| $str .= sprintf( ' height="%s"', $this->getImageHeight( $args ) ); | |
| $str .= ' />' . PHP_EOL; | |
| $str .= '</a>' . PHP_EOL; | |
| $str .= '<p class="text-center">'; | |
| $str .= sprintf( '<span class="name">%s</span>', $this->getMediaName( $args['name'] ) ); | |
| $str .= sprintf( ' <span class="size">%s</span>', $this->getMediaSize( $args ) ); | |
| $str .= '</p>' . PHP_EOL; | |
| $str .= '</div>' . PHP_EOL; | |
| return $str; | |
| } | |
| /** | |
| * Get the Video HTML. | |
| * | |
| * Types: jpg, png | |
| * | |
| * @param array $args | |
| * | |
| * @return string | |
| * @example <audio controls> | |
| * <source src="myAudio.mp3" type="audio/mp3"> | |
| * </audio> | |
| */ | |
| private function getAudioHtml( $args ) | |
| { | |
| $str = '<div class="media audio">' . PHP_EOL; | |
| $str .= '<audio controls' . PHP_EOL; | |
| $str .= sprintf( '<source src="%s" type="audio/mp3">%s', $args['src'], PHP_EOL ); | |
| $str .= '</audio>' . PHP_EOL; | |
| $str .= '<p class="text-center">'; | |
| $str .= sprintf( '<span class="name">%s</span>', $this->getMediaName( $args['name'] ) ); | |
| $str .= sprintf( ' <span class="size">%s</span>', $this->getMediaSize( $args ) ); | |
| $str .= '</p>' . PHP_EOL; | |
| $str .= '</div>' . PHP_EOL; | |
| return $str; | |
| } | |
| /** | |
| * Get the Video HTML. | |
| * | |
| * Types: mp4. | |
| * | |
| * @param array $args | |
| * | |
| * @return string | |
| */ | |
| private function getVideoHtml( $args ) | |
| { | |
| $str = '<div class="media video">' . PHP_EOL; | |
| $str .= '<video controls ' . PHP_EOL; | |
| $str .= sprintf( 'width="%s" ', $args['dim']['width'] ); | |
| $str .= sprintf( 'height="%s">%s', $args['dim']['height'], PHP_EOL ); | |
| $str .= sprintf( '<source src="%s" type="video/mp4">%s', $args['src'], PHP_EOL ); | |
| $str .= '</video>' . PHP_EOL; | |
| $str .= '<p class="text-center">'; | |
| $str .= sprintf( '<span class="name">%s</span>', $this->getMediaName( $args['name'] ) ); | |
| $str .= sprintf( ' <span class="size">%s</span>', $this->getMediaSize( $args ) ); | |
| $str .= '</p>' . PHP_EOL; | |
| $str .= '</div>' . PHP_EOL; | |
| return $str; | |
| } | |
| /** | |
| * Wrap the string in page HTML `<!DOCTYPE html>`, etc. | |
| * | |
| * @param string $str | |
| * @return string | |
| */ | |
| public function getPageHtml( $html, $args ) | |
| { | |
| $str = '<!DOCTYPE html>' . PHP_EOL; | |
| $str .= sprintf( '<html class="dynamic %s" lang="en-CA">', $args['type'], PHP_EOL ); | |
| $str .= '<head>' . PHP_EOL; | |
| $str .= '<meta charset="UTF-8">' . PHP_EOL; | |
| $str .= '<meta name="viewport" content="width=device-width, initial-scale=1"/>' . PHP_EOL; | |
| $str .= '<title>Media List</title>' . PHP_EOL; | |
| $str .= '<meta name="robots" content="noindex,nofollow" />' . PHP_EOL; | |
| $str .= '<link rel=stylesheet href="/0/theme/css/style.css">' . PHP_EOL; | |
| $str .= '</head>' . PHP_EOL; | |
| $str .= '<body>' . PHP_EOL; | |
| $str .= '<main>' . PHP_EOL; | |
| $str .= $html; | |
| $str .= '</main>' . PHP_EOL; | |
| $str .= '<footer>' . PHP_EOL; | |
| $str .= '<div class="text-center"><small>'; | |
| $str .= 'Note: This page has been <a href="https://github.com/earth3300/ec01-media-index.git">automatically generated</a>. No header, footer, menus or sidebars are available.'; | |
| $str .= '</small></div>' . PHP_EOL; | |
| $str .= '</footer>' . PHP_EOL; | |
| $str .= '</html>' . PHP_EOL; | |
| return $str; | |
| } | |
| /** | |
| * Get the Image Name and Dimensions from the String. | |
| * | |
| * Return the direct string values of each. Do no extra processing here. | |
| * | |
| * $regex = '\/([a-z\-]{3,60})-([0-9]{2,4}x[0-9]{2,4})' | |
| * | |
| * Although this looks complicated, it has potential to be helpful. | |
| * Not only does it divide the string given it into two parts, but it | |
| * also does a basic quality check on the image name structure. If the image | |
| * name does not meet the criteria given, it won't be captured. | |
| * | |
| * @param string $str | |
| * | |
| * @return array $arr['name'] $arr['dim'] | |
| * | |
| * @example $arr['name'] = 'image-name' | |
| * @example $arr['dim'] = '800x600' | |
| */ | |
| private function getImageNameDimArr( $str ) | |
| { | |
| /** | |
| * Since we won't have a valid image name with fewer than 13 characaters | |
| * we won't bother processing anything with less than that length. | |
| */ | |
| if ( strlen( $str ) > 12 ) | |
| { | |
| /** If this isn't matched, check for a name only */ | |
| $regex = '/\/([a-z\-]{3,150})-([0-9]{2,4}x[0-9]{2,4})/'; | |
| preg_match( $regex, $str, $match ); | |
| if ( empty( $match ) ) | |
| { | |
| $regex = '/\/([a-z,0-9\-]{5,150})\./'; | |
| preg_match( $regex, $str, $match ); | |
| } | |
| if ( ! empty( $match[1] ) ) | |
| { | |
| $arr['name'] = $match[1]; | |
| } | |
| else | |
| { | |
| $arr['name'] = null; | |
| } | |
| if ( ! empty( $match[2] ) ) | |
| { | |
| $arr['strDim'] = $match[2]; | |
| } | |
| else | |
| { | |
| $arr['strDim'] = null; | |
| } | |
| return $arr; | |
| } | |
| else { | |
| return false; | |
| } | |
| } | |
| /** | |
| * Get the image name. | |
| * | |
| * Gets the image name as the first part of the file name, before the | |
| * image size, if present. Converts hyphens into spaces and puts all | |
| * characters into uppercase, for simplicity. May be the same as the alt tag. | |
| * | |
| * The name: | |
| * | |
| * Starts with `/` | |
| * Ends with `-` | |
| * | |
| * $regex = '/\/([a-z\-]{3,36})-/'; | |
| * | |
| * @param array $args | |
| * @return string | |
| */ | |
| private function getImageNameStr( $str ) | |
| { | |
| if ( strlen( $str ) > 3 ) | |
| { | |
| $regex = '/\/([a-z\-]{3,36})--/'; | |
| preg_match( $regex, $str, $match ); | |
| if ( ! empty( $match[1] ) ) | |
| { | |
| return $match[1]; | |
| } | |
| else | |
| { | |
| // Not available. | |
| return $this->opts['msg']['na']; | |
| } | |
| } | |
| } | |
| /** | |
| * Get the image dimensions. | |
| * | |
| * Gets the image dimensions as the last part of the file name and only if | |
| * it follows the format: ##x##, where # is an integer. | |
| * | |
| * @param string $str | |
| * | |
| * @return string | |
| * | |
| * @example '1280x720' $dim['width'] = 1280, $dim['height'] = 720 | |
| */ | |
| private function getMediaDimArr( $str ) | |
| { | |
| if ( strlen( $str ) > 4 ) | |
| { | |
| $arr = explode( 'x', $str ); | |
| $dim['width'] = $arr[0]; | |
| $dim['height'] = $arr[1]; | |
| return $dim; | |
| } | |
| else | |
| { | |
| $dim['width'] = $this->opts['dim']['width']; | |
| $dim['height'] = $this->opts['dim']['height']; | |
| return $dim; | |
| } | |
| } | |
| /** | |
| * Get the image width. | |
| * | |
| * @param array $args | |
| * @return string | |
| */ | |
| private function getImageWidth( $args ) | |
| { | |
| if ( ! empty( $args['dim']['width'] ) ) | |
| { | |
| $width = $args['dim']['width']; | |
| } | |
| else if ( defined( 'SITE_IMAGE_WIDTH' ) ) | |
| { | |
| $width = SITE_IMAGE_WIDTH; | |
| } | |
| return $width; | |
| } | |
| /** | |
| * Get the image height. | |
| * | |
| * @param array $args | |
| * @return string | |
| */ | |
| private function getImageHeight( $args ) | |
| { | |
| if ( defined( 'SITE_IMAGE_HEIGHT' ) ) | |
| { | |
| $height = SITE_IMAGE_HEIGHT; | |
| } | |
| else | |
| { | |
| $height = $args['dim']['height'];; | |
| } | |
| return $height; | |
| } | |
| /** | |
| * Get the image size | |
| * | |
| * @param array $args | |
| * @return int | |
| */ | |
| private function getMediaSize( $args ){ | |
| if ( class_exists('Imagick') ) | |
| { | |
| $image = new Imagick( $args['file'] ); | |
| $size = $image->getImageLength(); | |
| } | |
| else | |
| { | |
| $size = filesize( $args['file'] ); | |
| } | |
| $size = number_format( $size / 1000, 1, ".", "," ); | |
| return $size . ' kB'; | |
| } | |
| /** | |
| * Get the image alt tag. | |
| * | |
| * May be the same as the name (or not). | |
| * | |
| * @param array $args | |
| * @return string | |
| */ | |
| private function getImageAlt( $args ) | |
| { | |
| if ( "" !== $args['src'] ) | |
| { | |
| $ex = explode( '/', $args['src'] ); | |
| $alt = $ex[ count( $ex ) - 1 ]; | |
| } | |
| else | |
| { | |
| $alt = $this->opts['msg']['na']; | |
| } | |
| return $alt; | |
| } | |
| /** | |
| * Get the Media Name | |
| * | |
| * @param array $args | |
| * @return string | |
| */ | |
| private function getMediaName( $str ) | |
| { | |
| if ( strlen( $str ) > 2 ) | |
| { | |
| $name = str_replace( '-', ' ', $str ); | |
| $name = strtoupper( $name ); | |
| } | |
| else | |
| { | |
| $name = $this->opts['msg']['na']; | |
| } | |
| return $name; | |
| } | |
| /** | |
| * Get the image class. | |
| * | |
| * @param array $args | |
| * @return string | |
| */ | |
| private function getImageClass( $args ) | |
| { | |
| if ( defined( 'SITE_IMAGE_CLASS' ) ) | |
| { | |
| $class = SITE_IMAGE_CLASS; | |
| } | |
| else | |
| { | |
| $class = 'generic'; | |
| } | |
| return $class; | |
| } | |
| /** | |
| * Check the arguments from the shortcode | |
| * | |
| * @param array $args | |
| * @return bool | |
| */ | |
| private function checkArgs( $args ) | |
| { | |
| if ( isset( $args['dir'] ) || isset( $args['self'] ) ) | |
| { | |
| return true; | |
| } | |
| else | |
| { | |
| return false; | |
| } | |
| } | |
| } | |
| /** | |
| * Callback from the media-list shortcode. | |
| * | |
| * Performs a check, then instantiates the MediaList class | |
| * and returns the media list as HTML. | |
| * | |
| * @param array $args['dir'] | |
| * @return string HTML as a list of images, wrapped in the article element. | |
| */ | |
| function media_list( $args ) | |
| { | |
| if ( is_array( $args ) ) | |
| { | |
| $media_list = new MediaList(); | |
| return $media_list -> get( $args ); | |
| } | |
| else | |
| { | |
| return '<!-- Missing the image directory to process. [media-list dir=""]-->'; | |
| } | |
| } | |
| /** | |
| * Check context (WordPress Plugin File or Directory Index File). | |
| * | |
| * The following checks to see whether or not this file (index.php) is being loaded | |
| * as part of the WordPress package, or not. If it is, we expect a WordPress | |
| * function to be available (in this case, `add_shortcode`). We then ensure there | |
| * is no direct access and add the shortcode hook, `media-list`. If we are not in | |
| * WordPress, then this file acts as an "indexing" type of file by listing all | |
| * of the allowed media types (currently jpg, png, mp3 and mp4) and making them | |
| * viewable to the end user by wrapping them in HTML and making use of a css | |
| * file that is expected to be found at `/0/media/theme/css/style.css`. This | |
| * idea was developed out of work to find a more robust method to develop out a | |
| * site, including that for a community. It makes use of the package found at: | |
| * {@link https://github.com/earth3300/ec01/wiki/}, with the entire codeset | |
| * available there through the same link. | |
| */ | |
| if( function_exists( 'add_shortcode' ) ) | |
| { | |
| // No direct access. | |
| defined('ABSPATH') || exit('No direct access.'); | |
| //shortcode [media-list dir=""] | |
| add_shortcode( 'media-list', 'media_list' ); | |
| } | |
| else | |
| { | |
| /** | |
| * Outside of WordPress. Instantiate directly, assuming current directory. | |
| * | |
| * @param $args['self'] = true List the files in the current directory. | |
| * | |
| * @param array $args['doctype'] = true Set the doctype to html. | |
| */ | |
| $media_list = new MediaList(); | |
| echo $media_list -> get( array( 'self' => true, 'doctype' => true ) ); | |
| } |
Author
Author
Edited to include video (mp4) and audio (mp3) files automatically. Also places a video or audio class respectively in the html element and then uses that to style the page in a dark theme for better viewing.
Author
Minor updates. Improved documentation.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Now edited so it can be used as the index.php file in a media directory to list the media files contained in that directory.