├── .gitignore ├── assets ├── icon-128x128.png ├── screenshot-1.png ├── screenshot-2.png ├── screenshot-3.png ├── screenshot-4.png └── banner-772x250.png ├── todo.txt ├── admin └── partials │ ├── image-meta-01.png │ ├── admin-general-header.php │ ├── admin-cache-header.php │ ├── admin-api-header.php │ └── admin-help-header.php ├── public ├── images │ ├── cc-attribution.png │ ├── cc-share-alike.png │ └── cc-non-commercial.png ├── css │ └── gga-dynamic-images.css └── partials │ └── image-attribution-item.php ├── uninstall.php ├── readme.txt ├── README.md ├── includes ├── class-gga-dynamic-placeholder-images-dashboard-widgets.php ├── class-gga-dynamic-placeholder-images-stats.php ├── class-gga-dynamic-placeholder-images-attachment-meta.php ├── class-gga-dynamic-placeholder-images-api.php ├── class-gga-dynamic-placeholder-images-attribution.php ├── class-gga-dynamic-placeholder-images-cache.php ├── class-gga-dynamic-placeholder-images-settings.php └── class-gga-dynamic-placeholder-images-core.php └── gga-dynamic-placeholder-images.php /.gitignore: -------------------------------------------------------------------------------- 1 | .phpintel 2 | -------------------------------------------------------------------------------- /assets/icon-128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/petenelson/gga-dynamic-placeholder-images/HEAD/assets/icon-128x128.png -------------------------------------------------------------------------------- /assets/screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/petenelson/gga-dynamic-placeholder-images/HEAD/assets/screenshot-1.png -------------------------------------------------------------------------------- /assets/screenshot-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/petenelson/gga-dynamic-placeholder-images/HEAD/assets/screenshot-2.png -------------------------------------------------------------------------------- /assets/screenshot-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/petenelson/gga-dynamic-placeholder-images/HEAD/assets/screenshot-3.png -------------------------------------------------------------------------------- /assets/screenshot-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/petenelson/gga-dynamic-placeholder-images/HEAD/assets/screenshot-4.png -------------------------------------------------------------------------------- /assets/banner-772x250.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/petenelson/gga-dynamic-placeholder-images/HEAD/assets/banner-772x250.png -------------------------------------------------------------------------------- /todo.txt: -------------------------------------------------------------------------------- 1 | Auto-clean cache directory to prevent it from getting larger than a configured size 2 | Min and max resolution configurable 3 | -------------------------------------------------------------------------------- /admin/partials/image-meta-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/petenelson/gga-dynamic-placeholder-images/HEAD/admin/partials/image-meta-01.png -------------------------------------------------------------------------------- /public/images/cc-attribution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/petenelson/gga-dynamic-placeholder-images/HEAD/public/images/cc-attribution.png -------------------------------------------------------------------------------- /public/images/cc-share-alike.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/petenelson/gga-dynamic-placeholder-images/HEAD/public/images/cc-share-alike.png -------------------------------------------------------------------------------- /public/images/cc-non-commercial.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/petenelson/gga-dynamic-placeholder-images/HEAD/public/images/cc-non-commercial.png -------------------------------------------------------------------------------- /admin/partials/admin-general-header.php: -------------------------------------------------------------------------------- 1 | 7 |

8 | View a random image: 9 |

-------------------------------------------------------------------------------- /admin/partials/admin-cache-header.php: -------------------------------------------------------------------------------- 1 | dashboard_widget_cache(); 9 | } 10 | 11 | -------------------------------------------------------------------------------- /uninstall.php: -------------------------------------------------------------------------------- 1 | prefix . 'gga_dynamic_images_stats'; 15 | $sql = "DROP TABLE IF EXISTS $table_name"; 16 | $wpdb->query($sql); 17 | 18 | $sql = "DELETE FROM $wpdb->options WHERE option_name like '_gga-placeholder-image-for%'"; 19 | $wpdb->query($sql); 20 | 21 | 22 | flush_rewrite_rules(); 23 | -------------------------------------------------------------------------------- /admin/partials/admin-api-header.php: -------------------------------------------------------------------------------- 1 | plugin_name . '-setting-is-enabled', 'images-api', $this->plugin_name . '-settings-api', 'api-enabled' ); 5 | $base_endpoint = apply_filters( $this->plugin_name . '-setting-get', 'images-api', $this->plugin_name . '-settings-api', 'api-endpoint' ); 6 | 7 | if ( ! empty( $enabled ) && ! empty( $base_endpoint ) ) { 8 | $url = site_url( trailingslashit( $base_endpoint ) ); 9 | } 10 | 11 | ?> 12 | 13 |

14 | 15 | 16 |
: 17 | 18 |

19 | -------------------------------------------------------------------------------- /public/css/gga-dynamic-images.css: -------------------------------------------------------------------------------- 1 | .gga-dynamic-images-attribution { 2 | margin: auto; 3 | } 4 | 5 | .gga-dynamic-images-attribution .attribImage { 6 | margin-top: 10px; 7 | display: inline-block; 8 | vertical-align: top; 9 | padding: 1%; 10 | width: 100%; 11 | text-align: center; 12 | box-sizing: border-box; 13 | } 14 | 15 | .gga-dynamic-images-attribution .attribImage-inner { 16 | display: inline-block; 17 | } 18 | 19 | 20 | .gga-dynamic-images-attribution .attribImage .image-meta { 21 | text-align: left; 22 | } 23 | 24 | 25 | .gga-dynamic-images-attribution .cc { 26 | display: inline-block; 27 | margin: 0.15em 28 | } 29 | 30 | .gga-dynamic-images-attribution .cc:first-of-type { 31 | margin-left: 0; 32 | } 33 | 34 | .gga-dynamic-images-attribution .cc:last-of-type { 35 | margin-right: 0.5em 36 | } 37 | 38 | .gga-dynamic-images-attribution .some-rights-reserved { 39 | font-size: 0.6em; 40 | } 41 | 42 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | === Dynamic Placeholder Images === 2 | Contributors: gungeekatx 3 | Tags: images, media 4 | Donate link: http://baconmockup.com 5 | Requires at least: 4.0 6 | Tested up to: 4.9 7 | Stable tag: 2.0.3 8 | License: GPLv2 or later 9 | License URI: http://www.gnu.org/licenses/gpl-2.0.html 10 | 11 | Serve dynamic placeholder images (like baconmockup or placekitten) 12 | 13 | == Description == 14 | 15 | If you've ever wanted to make your own dynamic placeholder images web site (like [baconmockup](http://baconmockup.com/) or [placekitten](https://placekitten.com/)), this plugin is for you. Once it's installed and you've added some images, you can access them at a URL with the desired height and width. Example: http://baconmockup.com/400/300/ 16 | 17 | 18 | Includes: 19 | 20 | * [dynamic-images-attribution] shortcode to display a complete list of images that can be used 21 | * JSON API 22 | 23 | Be sure to check Settings/Dynamic Placeholder images to fully customize your settings. 24 | 25 | View a demo page with my goofy dog: https://petenelson.com/dynamic-clyde-images/ 26 | 27 | Thanks to [Kenzie Moss](https://twitter.com/kenziemoss) for the plugin banner and icon. 28 | 29 | == Installation == 30 | 31 | 1. Upload the gga-dynamic-placeholder-images to the `/wp-content/plugins/` directory 32 | 2. Activate the plugin through the 'Plugins' menu in WordPress 33 | 3. Place the `[dynamic-images-attribution]` shortcode in a page or post 34 | 4. Check Settings/Dynamic Placeholder images to fully customize your settings. 35 | 36 | 37 | == Changelog == 38 | 39 | = v2.0.3 October 9, 2016 = 40 | * Fixed bug in 404 handling for URLs using specific slugs 41 | 42 | = v2.0.2 March 10, 2015 = 43 | * Fixed bugs in activation hook 44 | * Added deactivation hook to delete image cache 45 | * Updated uninstall hook to remove plugin options 46 | 47 | = v2.0.1 March 8, 2015 = 48 | * Minor updates to the image attribution CSS 49 | 50 | = v2.0.0 March 7, 2015 = 51 | * Initial release to the WordPress repository 52 | 53 | 54 | == Upgrade Notice == 55 | 56 | = v2.0.3 October 9, 2015 = 57 | * Fixed bug in 404 handling for URLs using specific slugs 58 | 59 | 60 | == Frequently Asked Questions == 61 | 62 | = Do you have any questions? = 63 | We can answer them here! 64 | 65 | 66 | == Screenshots == 67 | 68 | 1. Configure your dynamic placeholder image generator 69 | 2. Flag an image to be used as a placeholder image 70 | 3. Sample image 71 | 4. Sample image attributuon grid 72 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Dynamic Placeholder Images 2 | [WordPress plugin](https://wordpress.org/plugins/any-ipsum/) to manage and serve up dynamic placeholder images used by the [baconmockup](http://baconmockup.com/) web site 3 | 4 | [![Code Climate](https://codeclimate.com/github/petenelson/gga-dynamic-placeholder-images/badges/gpa.svg)](https://codeclimate.com/github/petenelson/gga-dynamic-placeholder-images) 5 | 6 | ## What's in here? 7 | 8 | - **gga-dynamic-placeholder-images.php** - WordPress plugin wrapper (loades classes, registers core hooks, etc) 9 | - **readme.txt** - Info for the WordPress repo 10 | - **uninstall.php** - WordPress uninstall code 11 | - **admin/partials** - Headers for the plugin settings 12 | - **assets/** - Assets for the WordPress plugin repository 13 | - **lang/** - For future translastions 14 | - **includes/** 15 | - **class-gga-dynamic-placeholder-images-core.php** - Exposes filters used by other classes 16 | - **class-gga-dynamic-placeholder-images-api.php** - API handler 17 | - **class-gga-dynamic-placeholder-images-attachment-meta.php** - Adds meta fields to Media library 18 | - **class-gga-dynamic-placeholder-images-attribution.php** - Handles the [dynamic-images-attribution] shortcode 19 | - **class-gga-dynamic-placeholder-images-cache.php** - Handles caching functions 20 | - **class-gga-dynamic-placeholder-images-dashboard-widgets.php** - Dashboard widget 21 | - **class-gga-dynamic-placeholder-images-settings.php** - Manages plugin settings 22 | - **class-gga-dynamic-placeholder-images-stats.php** - Manages stats logging 23 | - **public/** - CSS and images, and template for the attribution shortcode HTML 24 | 25 | 26 | ## Revision History 27 | 28 | ### v2.0.3 October 9, 2016 = 29 | - Fixed bug in 404 handling for URLs using specific slugs 30 | 31 | ### v2.0.2 March 10, 2015 32 | - Fixed bugs in activation hook 33 | - Added deactivation hook to delete image cache 34 | - Updated uninstall hook to remove plugin options 35 | 36 | ### v2.0.1 March 8, 2015 37 | - Minor updates to the image attribution CSS 38 | 39 | ### v2.0.0 March 7, 2015 40 | - Major rewrite, first release to the WordPress repository 41 | 42 | ### v1.2 March 6, 2013 43 | - Changed most options to autoload=no 44 | - Added textdomain support 45 | - Added admin option to clear associations created between dimensions and images 46 | - Added hook for delete_attachment to clear options assocation with a deleted image 47 | 48 | ### v1.1 March 6, 2013 49 | - Initial Release 50 | -------------------------------------------------------------------------------- /public/partials/image-attribution-item.php: -------------------------------------------------------------------------------- 1 | ID, $this->meta_prefix . 'attribute_to', true ); 6 | $attrib_url = get_post_meta( $gga_image_post->ID, $this->meta_prefix . 'attribute_url', true ); 7 | 8 | $cc_url = ''; 9 | 10 | if ( 'on' === get_post_meta( $gga_image_post->ID, $this->meta_prefix . 'cc_by', true ) ); 11 | $cc_url = 'http://creativecommons.org/licenses/by/2.0/'; 12 | 13 | if ( 'on' === get_post_meta( $gga_image_post->ID, $this->meta_prefix . 'cc_sa', true ) ); 14 | $cc_url = 'http://creativecommons.org/licenses/by-sa/2.0/'; 15 | 16 | $image_url = apply_filters( $this->plugin_name . '-image-url', '', $gga_attrib_args['width'], $gga_attrib_args['height'], $gga_image_post->post_name ); 17 | ?> 18 |
19 |
20 | <?php echo esc_attr( $gga_image_post->post_name ); ?> 21 |
22 |
tag: post_name ); ?>
23 | 24 |
by
25 | 26 | ID, $this->meta_prefix . 'cc_by', true ) ) { ?> 27 | cc_img_html( '', 'cc_by' ); ?> 28 | 29 | ID, $this->meta_prefix . 'cc_sa', true ) ) { ?> 30 | cc_img_html( '', 'cc_sa' ); ?> 31 | 32 | ID, $this->meta_prefix . 'cc_nc', true ) ) { ?> 33 | cc_img_html( '', 'cc_nc' ); ?> 34 | 35 | 36 | 37 | 38 |
39 |
40 |
41 | plugin_name . '-dashboard-widget', 20 | __('Dynamic Placeholder Images', 'gga-dynamic-placeholder-images' ), 21 | array( $this, 'dashboard_widget' ) 22 | ); 23 | 24 | } 25 | 26 | } 27 | 28 | 29 | public function dashboard_widget() { 30 | 31 | ?> 32 |
33 | 34 | dashboard_widget_cache( $show_help = true ); ?> 35 | 36 |
37 | 38 | plugin_name . '-get-cache-size', 0 ) ) ); 44 | if ( empty ( $cache_size ) ) { 45 | $cache_size = __( 'Empty', 'gga-dynamic-placeholder-images' ); 46 | } 47 | 48 | $image_associations = number_format( intval( apply_filters( $this->plugin_name . '-get-associations-count', 0 ) ) ); 49 | 50 | ?> 51 | 52 | 56 | 57 | 60 |

61 | 62 |

63 | 64 | 67 | 68 | plugin_name . '-action' => $action, 72 | $this->plugin_name . '-nonce' => wp_create_nonce( $action ), 73 | ) ); 74 | ?> 75 | 76 | 77 | 78 | plugin_name . '-action' => $action, 82 | $this->plugin_name . '-nonce' => wp_create_nonce( $action ), 83 | ) ); 84 | ?> 85 | 86 | 87 | 88 | update_db_check(); 15 | 16 | add_action( $this->plugin_name . '-image-view', array( $this, 'log_image_view' ) ); 17 | 18 | } 19 | 20 | 21 | public function activation_hook() { 22 | $this->create_stats_table(); 23 | } 24 | 25 | 26 | function update_db_check() { 27 | 28 | if ( get_site_option( $this->plugin_name . '-stats-version' ) != $this->version ) { 29 | $this->create_stats_table(); 30 | } 31 | 32 | } 33 | 34 | 35 | function stats_table_name() { 36 | global $wpdb; 37 | return $wpdb->prefix . 'gga_dynamic_images_stats'; 38 | } 39 | 40 | 41 | function create_stats_table() { 42 | // for reference 43 | // You must put each field on its own line in your SQL statement. 44 | // You must have two spaces between the words PRIMARY KEY and the definition of your primary key. 45 | // You must use the key word KEY rather than its synonym INDEX and you must include at least one KEY. 46 | // You must not use any apostrophes or backticks around field names. 47 | // Field types must be all lowercase. 48 | // SQL keywords, like CREATE TABLE and UPDATE, must be uppercase. 49 | 50 | global $wpdb; 51 | $table_name = $this->stats_table_name(); 52 | 53 | $charset_collate = $wpdb->get_charset_collate(); 54 | 55 | $sql = "CREATE TABLE $table_name ( 56 | id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT, 57 | time datetime NOT NULL DEFAULT '0000-00-00 00:00:00', 58 | post_id bigint(20) UNSIGNED NOT NULL, 59 | width int(11) UNSIGNED NOT NULL, 60 | height int(11) UNSIGNED NOT NULL, 61 | bytes int(11) UNSIGNED NOT NULL, 62 | PRIMARY KEY (id) 63 | ) $charset_collate;"; 64 | 65 | require_once( ABSPATH . 'wp-admin/includes/upgrade.php' ); 66 | dbDelta( $sql ); 67 | 68 | update_site_option( $this->plugin_name . '-stats-version', $this->version ); 69 | 70 | } 71 | 72 | 73 | function log_image_view( $args ) { 74 | 75 | $args = wp_parse_args( $args, array( 76 | 'post_id' => 0, 77 | 'width' => 0, 78 | 'height' => 0, 79 | 'bytes' => 0, 80 | )); 81 | 82 | global $wpdb; 83 | $table_name = $this->stats_table_name(); 84 | 85 | $wpdb->insert( 86 | $table_name, 87 | array( 88 | 'time' => current_time( 'mysql' ), 89 | 'post_id' => intval( $args['post_id'] ), 90 | 'width' => intval( $args['width'] ), 91 | 'height' => intval( $args['height'] ), 92 | 'bytes' => intval( $args['bytes'] ), 93 | ) 94 | ); 95 | 96 | $error = $wpdb->last_error; 97 | 98 | if ( strlen( trim( $error ) ) > 0 ) { 99 | $this->create_stats_table(); 100 | } 101 | 102 | 103 | } 104 | 105 | } 106 | 107 | 108 | } -------------------------------------------------------------------------------- /gga-dynamic-placeholder-images.php: -------------------------------------------------------------------------------- 1 | http://baconmockup.com/200/200) 6 | Version: 2.0.3 7 | Author: Pete Nelson (@GunGeekATX) 8 | Author URI: https://twitter.com/GunGeekATX 9 | Text Domain: gga-dynamic-placeholder-images 10 | Domain Path: /lang 11 | */ 12 | 13 | if ( ! defined( 'ABSPATH' ) ) die( 'restricted access' ); 14 | 15 | $includes = array( 'core', 'cache', 'api', 'attachment-meta', 'settings', 'dashboard-widgets', 'stats', 'attribution' ); 16 | foreach ($includes as $include) { 17 | require_once plugin_dir_path( __FILE__ ) . 'includes/class-gga-dynamic-placeholder-images-' . $include . '.php'; 18 | } 19 | 20 | 21 | // handles URL rewrites, generating placeholder images, flushing cache, etc 22 | if ( class_exists( 'GGA_Dynamic_Placeholder_Images_Core' ) ) { 23 | $gga_images_core = new GGA_Dynamic_Placeholder_Images_Core(); 24 | $gga_images_core->plugin_base_url = plugin_dir_url( __FILE__ ); 25 | add_action( 'plugins_loaded', array( $gga_images_core, 'plugins_loaded' ) ); 26 | } 27 | 28 | 29 | // Cache management 30 | if ( class_exists( 'GGA_Dynamic_Placeholder_Images_Cache' ) ) { 31 | $gga_images_cache = new GGA_Dynamic_Placeholder_Images_Cache(); 32 | add_action( 'plugins_loaded', array( $gga_images_cache, 'plugins_loaded' ) ); 33 | } 34 | 35 | 36 | // exposes an API endpoint and handles API actions 37 | if ( class_exists( 'GGA_Dynamic_Placeholder_Images_API' ) ) { 38 | $gga_images_api = new GGA_Dynamic_Placeholder_Images_API(); 39 | add_action( 'plugins_loaded', array( $gga_images_api, 'plugins_loaded' ) ); 40 | } 41 | 42 | 43 | // adds meta fields to attachments 44 | if ( class_exists( 'GGA_Dynamic_Placeholder_Images_Attachment_Meta' ) ) { 45 | $gga_images_meta = new GGA_Dynamic_Placeholder_Images_Attachment_Meta(); 46 | $gga_images_meta->plugin_base_url = plugin_dir_url( __FILE__ ); 47 | add_action( 'plugins_loaded', array( $gga_images_meta, 'plugins_loaded' ) ); 48 | } 49 | 50 | 51 | // handles Admin Settings pages and filters to get plugin settings 52 | if ( class_exists( 'GGA_Dynamic_Placeholder_Images_Settings' ) ) { 53 | $gga_images_settings = new GGA_Dynamic_Placeholder_Images_Settings(); 54 | $gga_images_settings->plugin_base_dir = plugin_dir_path( __FILE__ ); 55 | add_action( 'plugins_loaded', array( $gga_images_settings, 'plugins_loaded' ) ); 56 | register_activation_hook( __FILE__, array( $gga_images_settings, 'activation_hook' ) ); 57 | register_deactivation_hook( __FILE__, array( $gga_images_settings, 'deactivation_hook' ) ); 58 | } 59 | 60 | 61 | // Dashboard widgets 62 | if ( class_exists( 'GGA_Dynamic_Placeholder_Images_Dashboard_Widgets' ) ) { 63 | $gga_images_widgets = new GGA_Dynamic_Placeholder_Images_Dashboard_Widgets(); 64 | add_action( 'plugins_loaded', array( $gga_images_widgets, 'plugins_loaded' ) ); 65 | } 66 | 67 | 68 | // Stats logging 69 | if ( class_exists( 'GGA_Dynamic_Placeholder_Images_Stats' ) ) { 70 | $gga_images_stats = new GGA_Dynamic_Placeholder_Images_Stats(); 71 | add_action( 'plugins_loaded', array( $gga_images_stats, 'plugins_loaded' ) ); 72 | register_activation_hook( __FILE__, array( $gga_images_stats, 'activation_hook' ) ); 73 | } 74 | 75 | 76 | // Attribution shortcode 77 | if ( class_exists( 'GGA_Dynamic_Placeholder_Images_Attribution' ) ) { 78 | $gga_images_attrib = new GGA_Dynamic_Placeholder_Images_Attribution(); 79 | $gga_images_attrib->plugin_base_url = plugin_dir_url( __FILE__ ); 80 | $gga_images_attrib->plugin_base_dir = plugin_dir_path( __FILE__ ); 81 | add_action( 'plugins_loaded', array( $gga_images_attrib, 'plugins_loaded' ) ); 82 | } 83 | 84 | -------------------------------------------------------------------------------- /includes/class-gga-dynamic-placeholder-images-attachment-meta.php: -------------------------------------------------------------------------------- 1 | __( 'Is Dynamic Placeholder Image?', 'gga-dynamic-placeholder-images' ), 20 | 'input' => 'html', 21 | 'html' => $this->checkbox( $post, 'gga_image_is_mockup_image' ), 22 | 'value' => 'on', 23 | ); 24 | 25 | $form_fields['gga_image_attribute_to'] =array( 26 | 'label' => __( 'Attribute To', 'gga-dynamic-placeholder-images' ) . ':', 27 | 'value' => get_post_meta( $post->ID, '_gga_image_attribute_to', true ), 28 | ); 29 | 30 | $form_fields['gga_image_attribute_url'] =array( 31 | 'label' => __( 'Attribute Url', 'gga-dynamic-placeholder-images' ) . ':', 32 | 'value' => get_post_meta( $post->ID, '_gga_image_attribute_url', true ), 33 | ); 34 | 35 | $form_fields['gga_image_cc_by'] =array( 36 | 'label' => __( 'Creative Commons Attribution', 'gga-dynamic-placeholder-images' ) . ':', 37 | 'input' => 'html', 38 | 'html' => $this->checkbox( $post, 'gga_image_cc_by', array( 'append_html' => apply_filters( 'gga-dynamic-images-cc-img-html', '', 'cc_by' ) ) ), 39 | 'value' => 'on', 40 | ); 41 | 42 | $form_fields['gga_image_cc_sa'] =array( 43 | 'label' => __( 'Creative Commons Share Alike', 'gga-dynamic-placeholder-images' ) . ':', 44 | 'input' => 'html', 45 | 'html' => $this->checkbox( $post, 'gga_image_cc_sa', array( 'append_html' => apply_filters( 'gga-dynamic-images-cc-img-html', '', 'cc_sa' ) ) ), 46 | 'value' => 'on', 47 | ); 48 | 49 | $form_fields['gga_image_cc_nc'] =array( 50 | 'label' => __( 'Creative Commons Non-Commercial', 'gga-dynamic-placeholder-images' ) . ':', 51 | 'input' => 'html', 52 | 'html' => $this->checkbox( $post, 'gga_image_cc_nc', array( 'append_html' => apply_filters( 'gga-dynamic-images-cc-img-html', '', 'cc_nc' ) ) ), 53 | 'value' => 'on', 54 | ); 55 | 56 | 57 | return $form_fields; 58 | } 59 | 60 | 61 | function attachment_fields_to_save( $post, $attachment ) { 62 | 63 | $textfields = array( 'gga_image_attribute_to', 'gga_image_attribute_url' ); 64 | foreach ( $textfields as $field ) { 65 | if ( isset( $attachment[ $field ] ) ) { 66 | update_post_meta( $post['ID'], '_' . $field, sanitize_text_field( $attachment[ $field ] ) ); 67 | } 68 | } 69 | 70 | $checkboxes = array( 'gga_image_is_mockup_image', 'gga_image_cc_by', 'gga_image_cc_sa', 'gga_image_cc_nc' ); 71 | foreach ( $checkboxes as $field ) { 72 | if ( !empty ( $attachment[ $field ] ) && $attachment[ $field ] === 'on' ) { 73 | update_post_meta( $post['ID'], '_' . $field, 'on' ); 74 | } else { 75 | update_post_meta( $post['ID'], '_' . $field, '' ); 76 | } 77 | } 78 | 79 | return $post; 80 | 81 | } 82 | 83 | 84 | private function checkbox( $post, $id, $args = null ) { 85 | $meta_on = get_post_meta( $post->ID, '_' . $id, true ) === 'on'; 86 | $checked = $meta_on ? 'checked="checked"' : ''; 87 | $args = wp_parse_args( $args, array( 'append_html' => '' ) ); 88 | return "" . $args['append_html']; 89 | } 90 | 91 | 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /includes/class-gga-dynamic-placeholder-images-api.php: -------------------------------------------------------------------------------- 1 | plugin_name . '-setting-is-enabled', 'images-api', $this->plugin_name . '-settings-api', 'api-enabled' ); 19 | if ( $enabled ) { 20 | 21 | $endpoint = apply_filters( $this->plugin_name . '-setting-get', 'images-api', $this->plugin_name . '-settings-api', 'api-endpoint' ); 22 | if ( ! empty ( $endpoint ) ) { 23 | $endpoint .= '/'; 24 | } 25 | 26 | add_rewrite_tag( '%gga-image-api-action%', '([A-Za-z0-9\-\_]+)' ); 27 | add_rewrite_tag( '%gga-image-api-request%', '1' ); 28 | 29 | add_rewrite_rule( $endpoint . '([A-Za-z0-9\-\_]+)/?', 'index.php?gga-image-api-request=1&gga-image-api-action=$matches[1]', 'top' ); 30 | add_rewrite_rule( $endpoint . '?', 'index.php?gga-image-api-request=1', 'top' ); 31 | } 32 | } 33 | 34 | 35 | function template_redirect() { 36 | 37 | $enabled = apply_filters( $this->plugin_name . '-setting-is-enabled', 'images-api', $this->plugin_name . '-settings-api', 'api-enabled' ); 38 | if ( $enabled ) { 39 | global $wp_query; 40 | 41 | if ( $wp_query->get( 'gga-image-api-request' ) === '1' ) { 42 | 43 | $action = $wp_query->get( 'gga-image-api-action' ); 44 | $data = null; 45 | 46 | switch ( $action ) { 47 | 48 | case 'image-tags': 49 | $data = $this->image_tags_get(); 50 | break; 51 | 52 | default: 53 | $data = $this->endpoint_list_get(); 54 | break; 55 | } 56 | 57 | 58 | if ( empty ( $data) ) { 59 | wp_send_json_error(); 60 | } else { 61 | wp_send_json_success( $data ); 62 | } 63 | 64 | } 65 | 66 | 67 | } 68 | 69 | } 70 | 71 | 72 | private function endpoint_list_get() { 73 | 74 | $results = array(); 75 | $base_endpoint = apply_filters( $this->plugin_name . '-setting-get', 'images-api', $this->plugin_name . '-settings-api', 'api-endpoint' ); 76 | 77 | $endpoints = array( 78 | 'image-tags' => array( 79 | 'description' => __( 'Returns an array of available image tags', 'gga-dynamic-placeholder-images' ), 80 | ), 81 | ); 82 | 83 | 84 | foreach ( $endpoints as $endpoint => $details ) { 85 | $o = new stdClass(); 86 | $o->endpoint = $endpoint; 87 | $o->url = trim( site_url( trailingslashit( path_join( $base_endpoint, $endpoint ) ) ) ); 88 | $o->details = $details; 89 | $results[] = $o; 90 | } 91 | 92 | return $results; 93 | 94 | } 95 | 96 | 97 | private function image_tags_get() { 98 | 99 | $tags = array(); 100 | 101 | $args = array( 102 | 'posts_per_page' => -1, 103 | 'post_type' => 'attachment', 104 | 'post_status' => 'inherit', 105 | 'post_mime_type' => 'image', 106 | 'meta_key' => '_gga_image_is_mockup_image', 107 | 'meta_value' => 'on', 108 | 'orderby' => 'name', 109 | 'order' => 'asc', 110 | 'nopaging' => true, 111 | 'no_found_rows' => true, 112 | 'update_post_meta_cache' => false, 113 | 'update_post_term_cache' => false, 114 | ); 115 | 116 | 117 | global $post; 118 | $query = new WP_Query( $args ); 119 | while ( $query->have_posts() ) { 120 | $query->the_post(); 121 | $tags[] = $post->post_name; 122 | } 123 | 124 | wp_reset_postdata(); 125 | 126 | return $tags; 127 | 128 | } 129 | 130 | } 131 | 132 | 133 | } 134 | -------------------------------------------------------------------------------- /includes/class-gga-dynamic-placeholder-images-attribution.php: -------------------------------------------------------------------------------- 1 | plugin_name . '-cc-img-html', array( $this, 'cc_img_html' ), 10, 2 ); 22 | 23 | } 24 | 25 | 26 | public function image_attribution_shortcode( $args ) { 27 | 28 | global $gga_attrib_args; 29 | 30 | wp_enqueue_style( $this->plugin_name . '-attribution', $this->plugin_base_url . 'public/css/gga-dynamic-images.css', array(), $this->version ); 31 | $gga_attrib_args = $this->sanitize_args( wp_parse_args( $args, $this->default_shortcode_args() ) ); 32 | 33 | ob_start(); 34 | ?> 35 |
36 | query_posts( ); 38 | $this->echo_attribution_items_html( $posts ); 39 | ?> 40 |
41 | 42 | plugin_name . '-attribution-shortcode-html', $html, $posts ); 48 | } 49 | 50 | 51 | private function default_shortcode_args() { 52 | return array( 53 | 'width' => 300, 54 | 'height' => 300, 55 | 'columns' => 3, 56 | 'class' => 'gga-dynamic-images-attribution', 57 | ); 58 | } 59 | 60 | 61 | private function echo_attribution_items_html( $posts ) { 62 | global $gga_image_post; 63 | foreach( $posts as $gga_image_post ) { 64 | include $this->plugin_base_dir . 'public/partials/image-attribution-item.php'; 65 | } 66 | } 67 | 68 | 69 | public function cc_img_html( $html, $cc_type ) { 70 | 71 | $cc_data = array( 72 | 'cc_nc' => array( 73 | 'filename' => 'cc-non-commercial.png', 74 | 'alt' => __( 'Creative Commons Non-Commercial', 'gga-dynamic-placeholder-images' ), 75 | ), 76 | 'cc_by' => array( 77 | 'filename' => 'cc-attribution.png', 78 | 'alt' => __( 'Creative Commons Attribution', 'gga-dynamic-placeholder-images' ), 79 | ), 80 | 'cc_sa' => array( 81 | 'filename' => 'cc-share-alike.png', 82 | 'alt' => __( 'Creative Commons Share Alike', 'gga-dynamic-placeholder-images' ), 83 | ), 84 | ); 85 | 86 | if ( ! empty( $cc_data[ $cc_type ] ) ) { 87 | $html = '' . esc_attr( $cc_data[ $cc_type ]['alt'] ) . ''; 88 | } 89 | 90 | return $html; 91 | } 92 | 93 | 94 | private function query_posts( ) { 95 | $core = new GGA_Dynamic_Placeholder_Images_Core(); 96 | 97 | $query_args = $core->image_query_args(); 98 | $query_args['posts_per_page'] = -1; 99 | $query_args['orderby'] = 'name'; 100 | $query_args['order'] = 'asc'; 101 | 102 | return $core->query_images( $query_args ); 103 | } 104 | 105 | 106 | private function sanitize_args( $args ) { 107 | 108 | $int_args = array( 'width', 'height', 'columns' ); 109 | foreach ( $int_args as $key ) { 110 | $args[ $key ] = intval( $args[ $key ] ); 111 | } 112 | 113 | if ( $args['columns'] < 1 ) 114 | $args['columns'] = 1; 115 | 116 | if ( $args['width'] < 1 ) 117 | $args['width'] = 1; 118 | 119 | if ( $args['height'] < 1 ) 120 | $args['height'] = 1; 121 | 122 | $args['class'] = esc_attr( $args['class'] ); 123 | 124 | return $args; 125 | 126 | } 127 | 128 | 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /includes/class-gga-dynamic-placeholder-images-cache.php: -------------------------------------------------------------------------------- 1 | plugin_name . '-get-cache-directory', array( $this, 'get_cache_directory' ) ); 14 | add_filter( $this->plugin_name . '-get-cache-directory-contents', array( $this, 'get_cache_directory_contents' ) ); 15 | add_filter( $this->plugin_name . '-get-cache-size', array( $this, 'get_cache_directory_size' ) ); 16 | 17 | add_action( $this->plugin_name . '-purge-cache', array( $this, 'purge_cache_directory' ) ); 18 | 19 | } 20 | 21 | 22 | private function init_filesystem() { 23 | require_once( ABSPATH . 'wp-admin/includes/file.php' ); 24 | $access_type = get_filesystem_method(); 25 | if( $access_type === 'direct' ) { 26 | $upload_dir = wp_upload_dir(); 27 | $creds = request_filesystem_credentials( $upload_dir['url'] ); 28 | /* initialize the API */ 29 | return WP_Filesystem( $creds ); 30 | } else { 31 | return false; 32 | } 33 | } 34 | 35 | 36 | public function get_cache_directory( $cache_directory = '' ) { 37 | $cache_directory = apply_filters( $this->plugin_name . '-setting-get', 'gga-dynamic-placeholder-images', $this->plugin_name . '-settings-cache', 'cache-directory' ); 38 | $upload_dir = wp_upload_dir(); 39 | $cache_directory = path_join( $upload_dir['basedir'], $cache_directory ); 40 | return $cache_directory; 41 | } 42 | 43 | 44 | public function get_cache_directory_contents( $contents = false ) { 45 | if ( $this->init_filesystem() ) { 46 | $cache_directory = $this->get_cache_directory(); 47 | global $wp_filesystem; 48 | $contents = $wp_filesystem->dirlist( $cache_directory, false, true ); 49 | } 50 | return $contents; 51 | } 52 | 53 | 54 | public function get_cache_directory_size( $size ) { 55 | $transient = $this->plugin_name . '-cache-size'; 56 | $size = get_site_transient( $transient ); 57 | if ( ! empty( $size ) ) { 58 | return $size; 59 | } 60 | 61 | $list = $this->get_cache_directory_contents(); 62 | if ( ! empty( $list ) ) { 63 | $size = $this->get_directory_size( $list ); 64 | set_site_transient( $transient, $size, MINUTE_IN_SECONDS * 15 ); 65 | return $size; 66 | } else { 67 | return $size; 68 | } 69 | 70 | } 71 | 72 | 73 | public function purge_cache_directory() { 74 | if ( $this->delete_cache_directory() ) { 75 | return $this->create_cache_directory(); 76 | } 77 | else { 78 | return false; 79 | } 80 | } 81 | 82 | 83 | private function create_cache_directory() { 84 | $cache_directory = $this->get_cache_directory(); 85 | if ( wp_mkdir_p( $cache_directory ) ) { 86 | foreach( $this->cache_width_directories() as $width ) { 87 | $this->create_cache_width_directory( $cache_directory, $width ); 88 | } 89 | } 90 | 91 | } 92 | 93 | 94 | private function create_cache_width_directory( $base_cache_directory, $width ) { 95 | wp_mkdir_p( path_join( $base_cache_directory, $width ) ); 96 | } 97 | 98 | 99 | public function delete_cache_directory() { 100 | 101 | if ( $this->init_filesystem() ) { 102 | global $wp_filesystem; 103 | $cache_directory = $this->get_cache_directory(); 104 | delete_site_transient( $this->plugin_name . '-cache-size' ); 105 | return $wp_filesystem->rmdir( $cache_directory, true ); 106 | } else { 107 | return false; 108 | } 109 | 110 | } 111 | 112 | 113 | private function get_directory_size( $list ) { 114 | $size = 0; 115 | 116 | if ( ! empty( $list ) ) { 117 | 118 | foreach ($list as $key => $item) { 119 | if ( $item['type'] == 'f' ) { 120 | $size += $item['size']; 121 | } else if ( $item['type'] == 'd' && ! empty( $item['files'] ) ) { 122 | $size += $this->get_directory_size( $item['files'] ); 123 | } 124 | } 125 | 126 | } 127 | 128 | return $size; 129 | } 130 | 131 | 132 | private function cache_width_directories() { 133 | $widths = array(); 134 | for ( $width = 0; $width <= 2000; $width += 100 ) { 135 | $widths[] = $width; 136 | } 137 | return $widths; 138 | } 139 | 140 | 141 | 142 | } 143 | 144 | } -------------------------------------------------------------------------------- /admin/partials/admin-help-header.php: -------------------------------------------------------------------------------- 1 | plugin_name . '-setting-is-enabled', 'images-api', $this->plugin_name . '-settings-api', 'api-enabled' ); 5 | $base_endpoint = apply_filters( $this->plugin_name . '-setting-get', 'images-api', $this->plugin_name . '-settings-api', 'api-endpoint' ); 6 | 7 | if ( ! empty( $enabled ) && ! empty( $base_endpoint ) ) { 8 | $url = site_url( trailingslashit( $base_endpoint ) ); 9 | } 10 | 11 | 12 | ?> 13 | 24 | 25 |
26 | 27 |

28 |

29 | 30 |

31 | 32 |

33 | 34 | 35 |
36 | <?php _e( 'Image meta', 'gga-dynamic-placeholder-images' ); ?> 37 |

38 | 39 | 40 |

41 |

42 | : [dynamic-images-attribution]
43 | 44 |
45 |
46 | columns -
47 | width -
48 | height -
49 | class -
50 |
51 | : [dynamic-images-attribution columns=4 width=200 height=200 class='my-image-attributions'] 52 |

53 | 54 | 55 |

56 |

57 | gga-dynamic-placeholder-images folder in the uploads folder.', 'gga-dynamic-placeholder-images' ) ?> 58 | 59 | Cache tab to clear up disk space.', 'gga-dynamic-placeholder-images' ), admin_url( 'options-general.php?page=gga-dynamic-images-settings&tab=gga-dynamic-images-settings-cache' ) ) ?> 60 | 61 | 62 | 63 |

64 | 65 |

66 | 67 | 68 | 69 |

70 | 102 |

103 |

104 | 105 |

106 |

107 | : pete@petenelson.com
108 | : @GunGeekATX
109 | : https://github.com/petenelson/gga-dynamic-placeholder-images
110 |

111 | 112 |
113 | -------------------------------------------------------------------------------- /includes/class-gga-dynamic-placeholder-images-settings.php: -------------------------------------------------------------------------------- 1 | plugin_name . '-setting-is-enabled', array( $this, 'setting_is_enabled' ), 10, 3 ); 24 | add_filter( $this->plugin_name . '-setting-get', array( $this, 'setting_get' ), 10, 3 ); 25 | 26 | // admin menus 27 | if ( is_admin() ) { 28 | $this->handle_admin_actions(); 29 | add_action( 'admin_init', array( $this, 'admin_init' ) ); 30 | add_action( 'admin_menu', array( $this, 'admin_menu' ) ); 31 | add_action( 'admin_notices', array( $this, 'activation_admin_notice' ) ); 32 | } 33 | 34 | } 35 | 36 | 37 | private function get_request( $key, $default = '', $filter = FILTER_SANITIZE_STRING ) { 38 | return GGA_Dynamic_Placeholder_Images_Core::get_request( $key, $default, $filter ); 39 | } 40 | 41 | 42 | public function activation_hook() { 43 | 44 | // create default settings 45 | add_option( $this->settings_key_general, array( 46 | 'name' => 'Dynamic Images', 47 | 'base-url' => 'dynamic-images', 48 | ), '', $autoload = 'no' ); 49 | 50 | add_option( $this->settings_key_api, array( 51 | 'api-enabled' => '0', 52 | 'api-endpoint' => 'images-api', 53 | ), '', $autoload = 'no' ); 54 | 55 | 56 | // add an option so we can show the activated admin notice 57 | add_option( $this->plugin_name . '-plugin-activated', '1' ); 58 | 59 | if ( class_exists( 'GGA_Dynamic_Placeholder_Images_Core' ) ) { 60 | $core = new GGA_Dynamic_Placeholder_Images_Core(); 61 | $core->register_rewrites( 'dynamic-images/' ); 62 | } 63 | 64 | flush_rewrite_rules( ); 65 | 66 | } 67 | 68 | 69 | public function deactivation_hook() { 70 | if ( class_exists( 'GGA_Dynamic_Placeholder_Images_Cache' ) ) { 71 | $cache = new GGA_Dynamic_Placeholder_Images_Cache(); 72 | $cache->delete_cache_directory(); 73 | } 74 | } 75 | 76 | 77 | function activation_admin_notice() { 78 | if ( '1' === get_option( $this->plugin_name . '-plugin-activated' ) ) { 79 | ?> 80 |
81 |

Dynamic Placeholder Images activated! Please visit the Settings page to customize your image generator.', 'gga-dynamic-placeholder-images' ), admin_url( 'options-general.php?page=' . $this->plugin_name . '-settings' ) ); 83 | ?>

84 |
85 | plugin_name . '-plugin-activated' ); 87 | } 88 | } 89 | 90 | 91 | function handle_admin_action_notices() { 92 | global $gga_dynamic_images_admin_notice; 93 | if ( ! empty( $gga_dynamic_images_admin_notice ) ) { 94 | ?> 95 |
96 |

:

97 |
98 | get_request( $this->plugin_name . '-action' ); 106 | $nonce = $this->get_request( $this->plugin_name . '-nonce' ); 107 | if ( ! empty( $action ) && current_user_can( 'manage_options' ) && wp_verify_nonce( $nonce, $action ) ) { 108 | 109 | global $gga_dynamic_images_admin_notice; 110 | 111 | switch ( $action ) { 112 | case 'purge-cache': 113 | do_action( $this->plugin_name . '-purge-cache' ); 114 | $gga_dynamic_images_admin_notice = __( 'Cache Purged', 'gga-dynamic-placeholder-images' ); 115 | break; 116 | case 'delete-associations': 117 | do_action( $this->plugin_name . '-delete-associations' ); 118 | $gga_dynamic_images_admin_notice = __( 'Image Associations Deleted', 'gga-dynamic-placeholder-images' ); 119 | break; 120 | } 121 | 122 | add_action( 'admin_notices', array( $this, 'handle_admin_action_notices' ) ); 123 | 124 | } 125 | 126 | } 127 | 128 | 129 | function admin_init() { 130 | $this->register_general_settings(); 131 | $this->register_api_settings(); 132 | $this->register_cache_settings(); 133 | $this->register_help_tab(); 134 | } 135 | 136 | 137 | function register_general_settings() { 138 | $key = $this->settings_key_general; 139 | $this->plugin_settings_tabs[$key] = __( 'General', 'gga-dynamic-placeholder-images' ); 140 | 141 | register_setting( $key, $key, array( $this, 'general_settings_sanitize' ) ); 142 | 143 | $section = 'general'; 144 | 145 | add_settings_section( $section, '', array( $this, 'section_header' ), $key ); 146 | 147 | $permalink_structure = get_option( 'permalink_structure' ); 148 | $permalink_warning = empty($permalink_structure) ? ' (please anable any non-default Permalink structure)' : ''; 149 | 150 | /* 151 | add_settings_field( 'name', __( 'Your Dynamic Placholder Name', 'gga-dynamic-placeholder-images'), array( $this, 'settings_input' ), $key, $section, 152 | array('key' => $key, 'name' => 'name', 'size' => 20, 'maxlength' => 50, 'after' => 'Example: Bacon Mockup, Place Kitten')); 153 | */ 154 | 155 | 156 | $after = __( 'Example: dynamic-images (ex: /dynamic-images/300/300/) or leave blank to operate at the root of your site.

NOTE: A blank URL may prevent other numeric-based URLs from working. Use with caution.', 'gga-dynamic-placeholder-images' ) . $permalink_warning; 157 | 158 | add_settings_field( 'base-url', __( 'Base URL', 'gga-dynamic-placeholder-images' ), array( $this, 'settings_input' ), $key, $section, 159 | array('key' => $key, 'name' => 'base-url', 'size' => 20, 'maxlength' => 50, 'after' => $after)); 160 | 161 | } 162 | 163 | 164 | function general_settings_sanitize( $settings ) { 165 | 166 | if ( isset( $settings['base-url'] ) ) { 167 | $settings['base-url'] = sanitize_key( $settings['base-url'] ); 168 | } 169 | 170 | return $settings; 171 | } 172 | 173 | 174 | function register_api_settings() { 175 | $key = $this->settings_key_api; 176 | $this->plugin_settings_tabs[$key] = __('API', 'gga-dynamic-placeholder-images'); 177 | 178 | register_setting( $key, $key, array( $this, 'api_settings_sanitize') ); 179 | 180 | $section = 'api'; 181 | 182 | add_settings_section( $section, '', array( $this, 'section_header' ), $key ); 183 | 184 | add_settings_field( 'api-enabled', __('Enabled', 'gga-dynamic-placeholder-images'), array( $this, 'settings_yes_no' ), $key, $section, 185 | array('key' => $key, 'name' => 'api-enabled')); 186 | 187 | $permalink_structure = get_option( 'permalink_structure' ); 188 | $permalink_warning = empty($permalink_structure) ? ' (please anable any non-default Permalink structure)' : ''; 189 | 190 | add_settings_field( 'api-endpoint', __('Endpoint Page Name', 'gga-dynamic-placeholder-images'), array( $this, 'settings_input' ), $key, $section, 191 | array('key' => $key, 'name' => 'api-endpoint', 'size' => 20, 'maxlength' => 50, 'after' => 'Example: images-api, bacon-images-api, etc' . $permalink_warning)); 192 | 193 | } 194 | 195 | 196 | function api_settings_sanitize( $settings ) { 197 | 198 | if ( isset( $settings['api-endpoint'] ) ) { 199 | $settings['api-endpoint'] = sanitize_key( $settings['api-endpoint'] ); 200 | } 201 | 202 | return $settings; 203 | } 204 | 205 | 206 | function register_cache_settings() { 207 | $key = $this->settings_key_cache; 208 | $this->plugin_settings_tabs[$key] = __( 'Cache', 'gga-dynamic-placeholder-images' ); 209 | 210 | register_setting( $key, $key ); 211 | 212 | $section = 'cache'; 213 | 214 | add_settings_section( $section, '', array( $this, 'section_header' ), $key ); 215 | 216 | } 217 | 218 | 219 | function register_help_tab() { 220 | $key = $this->settings_key_help; 221 | $this->plugin_settings_tabs[$key] = __('Help', 'gga-dynamic-placeholder-images'); 222 | 223 | register_setting( $key, $key ); 224 | 225 | $section = 'help'; 226 | 227 | add_settings_section( $section, '', array( $this, 'section_header' ), $key ); 228 | 229 | } 230 | 231 | 232 | function setting_is_enabled( $enabled, $key, $setting ) { 233 | return '1' === $this->setting_get('0', $key, $setting); 234 | } 235 | 236 | 237 | function setting_get( $value, $key, $setting ) { 238 | 239 | $args = wp_parse_args( get_option($key), 240 | array( 241 | $setting => $value, 242 | ) 243 | ); 244 | 245 | return $args[ $setting ]; 246 | } 247 | 248 | 249 | function settings_input($args) { 250 | 251 | $args = wp_parse_args( $args, 252 | array( 253 | 'name' => '', 254 | 'key' => '', 255 | 'maxlength' => 50, 256 | 'size' => 30, 257 | 'after' => '', 258 | ) 259 | ); 260 | 261 | 262 | $name = $args['name']; 263 | $key = $args['key']; 264 | $size = $args['size']; 265 | $maxlength = $args['maxlength']; 266 | 267 | $option = get_option($key); 268 | $value = isset($option[$name]) ? esc_attr($option[$name]) : ''; 269 | 270 | echo "
"; 271 | if (!empty($args['after'])) 272 | echo '
' . __($args['after'], 'gga-dynamic-placeholder-images') . '
'; 273 | 274 | } 275 | 276 | 277 | function settings_textarea($args) { 278 | 279 | $args = wp_parse_args( $args, 280 | array( 281 | 'name' => '', 282 | 'key' => '', 283 | 'rows' => 10, 284 | 'cols' => 40, 285 | 'after' => '', 286 | ) 287 | ); 288 | 289 | 290 | $name = $args['name']; 291 | $key = $args['key']; 292 | $rows = $args['rows']; 293 | $cols = $args['cols']; 294 | 295 | $option = get_option($key); 296 | $value = isset($option[$name]) ? esc_attr($option[$name]) : ''; 297 | 298 | echo "
"; 299 | if (!empty($args['after'])) 300 | echo '
' . $args['after'] . '
'; 301 | 302 | } 303 | 304 | 305 | function settings_yes_no($args) { 306 | 307 | $args = wp_parse_args( $args, 308 | array( 309 | 'name' => '', 310 | 'key' => '', 311 | 'after' => '', 312 | ) 313 | ); 314 | 315 | $name = $args['name']; 316 | $key = $args['key']; 317 | 318 | $option = get_option($key); 319 | $value = isset($option[$name]) ? esc_attr($option[$name]) : ''; 320 | 321 | if (empty($value)) 322 | $value = '0'; 323 | 324 | echo '
'; 325 | echo " "; 326 | echo " "; 327 | echo '
'; 328 | 329 | if (!empty($args['after'])) 330 | echo '
' . __($args['after'], 'gga-dynamic-placeholder-images') . '
'; 331 | } 332 | 333 | 334 | function admin_menu() { 335 | add_options_page( __('Dynamic Placeholder Images', 'gga-dynamic-placeholder-images' ), __('Dynamic Placeholder Images', 'gga-dynamic-placeholder-images'), 'manage_options', $this->settings_page, array($this, 'options_page' ), 30); 336 | } 337 | 338 | 339 | function options_page() { 340 | 341 | $tab = filter_input(INPUT_GET, 'tab', FILTER_SANITIZE_STRING); 342 | if ( empty ( $tab ) ) { 343 | $tab = $this->settings_key_general; 344 | } 345 | 346 | ?> 347 |
348 | plugin_options_tabs(); ?> 349 |
350 | 351 | 352 | settings_key_help !== $tab && $tab !== $this->settings_key_cache ) { 354 | submit_button(__('Save Settings', 'gga-dynamic-placeholder-images'), 'primary', 'submit', true); 355 | } 356 | ?> 357 |
358 |
359 | get_request( 'settings-updated' ); 362 | if ( ! empty( $settings_updated ) ) { 363 | flush_rewrite_rules( ); 364 | } 365 | 366 | } 367 | 368 | 369 | function plugin_options_tabs() { 370 | $current_tab = $this->get_request( 'tab' ); 371 | if ( empty( $current_tab ) ) { 372 | $current_tab = $this->settings_key_general; 373 | } 374 | echo '

' . __('Dynamic Placeholder Images Settings', 'gga-dynamic-placeholder-images') . '

'; 380 | } 381 | 382 | 383 | function section_header( $args ) { 384 | 385 | switch ( $args['id'] ) { 386 | case 'general'; 387 | include_once $this->plugin_base_dir . 'admin/partials/admin-general-header.php'; 388 | break; 389 | case 'cache'; 390 | include_once $this->plugin_base_dir . 'admin/partials/admin-cache-header.php'; 391 | break; 392 | case 'api'; 393 | include_once $this->plugin_base_dir . 'admin/partials/admin-api-header.php'; 394 | break; 395 | case 'help'; 396 | include_once $this->plugin_base_dir . 'admin/partials/admin-help-header.php'; 397 | break; 398 | } 399 | 400 | } 401 | 402 | 403 | } 404 | 405 | } 406 | -------------------------------------------------------------------------------- /includes/class-gga-dynamic-placeholder-images-core.php: -------------------------------------------------------------------------------- 1 | plugin_name . '-image-url', array( $this, 'generate_image_url' ), 10, 4 ); 38 | 39 | add_action( $this->plugin_name . '-delete-associations', array( $this, 'delete_all_dimension_associations' ) ); 40 | add_filter( $this->plugin_name . '-get-associations-count', array( $this, 'get_dimension_associations_count' ) ); 41 | 42 | } 43 | 44 | 45 | public function register_rewrites( $base_url = '' ) { 46 | 47 | if ( empty( $base_url ) ) { 48 | $base_url = $this->get_base_url(); 49 | } 50 | 51 | add_rewrite_tag( '%gga-dynamic-image%', '1' ); 52 | add_rewrite_tag( '%gga-dynamic-image-width%', '([0-9]+)' ); 53 | add_rewrite_tag( '%gga-dynamic-image-height%', '([0-9]+)' ); 54 | add_rewrite_tag( '%gga-dynamic-image-slug%', '([A-Za-z0-9\-\_]+)' ); 55 | 56 | add_rewrite_rule( $base_url . '([0-9]+)/([0-9]+)/([A-Za-z0-9\-\_]+)/?', 'index.php?gga-dynamic-image=1&gga-dynamic-image-width=$matches[1]&gga-dynamic-image-height=$matches[2]&gga-dynamic-image-slug=$matches[3]', 'top' ); 57 | add_rewrite_rule( $base_url . '([0-9]+)/([0-9]+)/?', 'index.php?gga-dynamic-image=1&gga-dynamic-image-width=$matches[1]&gga-dynamic-image-height=$matches[2]&gga-dynamic-image-slug=', 'top' ); 58 | } 59 | 60 | 61 | public function template_redirect() { 62 | 63 | global $wp_query; 64 | 65 | $action = $wp_query->get( 'gga-dynamic-image' ); 66 | 67 | if ( ! empty( $action ) ) { 68 | $width = intval( $wp_query->get( 'gga-dynamic-image-width' ) ); 69 | $height = intval( $wp_query->get( 'gga-dynamic-image-height' ) ); 70 | $slug = sanitize_key( $wp_query->get( 'gga-dynamic-image-slug' ) ); 71 | 72 | // bounds checking, defaults to a max of 2000x2000 73 | $max_width = $this->get_max_width(); 74 | $max_height = $this->get_max_height(); 75 | $width = $width > $max_width ? $max_width : $width; 76 | $height = $height > $max_height ? $max_height : $height; 77 | 78 | $this->handle_dynamic_image( $width, $height, $slug ); 79 | } 80 | 81 | } 82 | 83 | public function generate_image_url( $url, $width, $height, $tag = '' ) { 84 | $url = site_url( $this->get_base_url() . intval( $width ) . '/' . intval( $height ) . '/' . sanitize_key( $tag ) . '/' ); 85 | return $url; 86 | } 87 | 88 | 89 | private function get_base_url( $base_url = '' ) { 90 | $base_url = apply_filters( $this->plugin_name . '-setting-get', 'dynamic-image', 'gga-dynamic-images-settings-general', 'base-url' ); 91 | return ! empty( $base_url ) ? $base_url . '/' : ''; 92 | } 93 | 94 | 95 | private function handle_dynamic_image( $width, $height, $slug ) { 96 | 97 | if ( $width !== 0 && $height !== 0 ) { 98 | 99 | $id = $this->get_image_id_for_slug( $slug, $width, $height ); 100 | 101 | // Set the 404 status if the slug isn't valid. 102 | if ( ! empty( $slug ) && 'random' !== $slug && empty( $id ) ) { 103 | $this->set_404(); 104 | return; 105 | } 106 | 107 | if ( empty( $id ) ) { 108 | $id = $this->get_random_image_id(); 109 | } 110 | 111 | if ( empty( $id ) ) { 112 | $this->set_404(); 113 | } 114 | else { 115 | $this->generate_image( $id, $width, $height ); 116 | $this->stream_image( $id, $width, $height ); 117 | } 118 | } 119 | 120 | } 121 | 122 | 123 | private function get_image_id_for_slug( $slug, $width, $height ) { 124 | $id = 0; 125 | 126 | if ( empty( $slug ) ) { 127 | $id = $this->get_existing_image_id_by_dimensions( $width, $height ); 128 | } else if ( $slug === 'random' ) { 129 | $id = $this->get_random_image_id(); 130 | $this->add_expires = false; 131 | } else if ( ! empty( $slug ) ) { 132 | $id = $this->get_image_id_by_slug( $slug ); 133 | if ( empty( $id ) ) { 134 | $this->set_404(); 135 | } 136 | } 137 | 138 | return $id; 139 | } 140 | 141 | 142 | public function delete_attachment( $postid ) { 143 | $this->delete_options_from_query( " WHERE option_name like '_gga-placeholder-image-for%' and option_value = '" . intval( $postid ) . "'" ); 144 | } 145 | 146 | 147 | public function delete_all_dimension_associations() { 148 | if ( current_user_can( 'manage_options' ) ) { 149 | $this->delete_options_from_query( " WHERE option_name like '_gga-placeholder-image-for%'" ); 150 | } 151 | } 152 | 153 | 154 | public function get_dimension_associations_count( $count ) { 155 | global $wpdb; 156 | $count = $wpdb->get_var( "SELECT count(*) FROM $wpdb->options WHERE option_name like '_gga-placeholder-image-for%'" ); 157 | return $count; 158 | } 159 | 160 | 161 | private function delete_options_from_query( $query ) { 162 | if ( ! empty( $query) ) { 163 | global $wpdb; 164 | $results = $wpdb->get_results( "SELECT option_name, option_value FROM $wpdb->options " . $query ); 165 | 166 | foreach ( $results as $r ) { 167 | delete_option( $r->option_name ); 168 | } 169 | } 170 | } 171 | 172 | private function set_404() { 173 | global $wp_query; 174 | $wp_query->is_404 = true; 175 | } 176 | 177 | public function query_images( $args ) { 178 | global $post; 179 | $posts = array(); 180 | $query = new WP_Query( $args ); 181 | 182 | while ( $query->have_posts() ) { 183 | $query->the_post(); 184 | $posts[] = $post; 185 | } 186 | 187 | wp_reset_postdata(); 188 | return $posts; 189 | } 190 | 191 | 192 | public function image_query_args() { 193 | 194 | return array( 195 | 'posts_per_page' => 1, 196 | 'post_type' => 'attachment', 197 | 'post_status' => 'inherit', 198 | 'post_mime_type' => 'image', 199 | 'meta_key' => '_gga_image_is_mockup_image', 200 | 'meta_value' => 'on', 201 | ); 202 | 203 | } 204 | 205 | 206 | private function get_random_image_id() { 207 | $args = $this->image_query_args(); 208 | $args['orderby'] = 'rand'; 209 | return $this->get_image_id_from_query( $args ); 210 | } 211 | 212 | 213 | private function get_image_id_by_slug( $slug ) { 214 | $args = $this->image_query_args(); 215 | $args['name'] = $slug; 216 | return $this->get_image_id_from_query( $args ); 217 | } 218 | 219 | 220 | private function get_image_id_from_query( $args ) { 221 | $id = 0; 222 | $query = new WP_Query( $args ); 223 | 224 | if ( $query->have_posts() ) { 225 | $id = $query->post->ID; 226 | } 227 | 228 | return $id; 229 | } 230 | 231 | 232 | private function stream_image( $id, $width, $height ) { 233 | 234 | $image_size_name = $this->image_size_name( $width, $height ); 235 | $image = get_post( $id ); 236 | 237 | $meta = wp_get_attachment_metadata( $id ); 238 | if ( ! empty( $meta[ $this->meta_sizes ] ) ) { 239 | $sizes = $meta[ $this->meta_sizes ]; 240 | } 241 | 242 | if ( !empty( $sizes ) && ! empty( $sizes[ $image_size_name ] ) ) { 243 | 244 | $filename = $this->get_cached_file_path( $sizes[ $image_size_name ]['file'], $width ); 245 | if ( ! file_exists( $filename ) ) { 246 | // regenrate a missing image 247 | $this->generate_image( $id, $width, $height ); 248 | } 249 | 250 | $filesize = filesize( $filename ); 251 | $this->send_image_stream_headers( $sizes[$image_size_name]['mime-type'], $filesize, $image->post_name . '-' . $width . '-' . $height . '.jpg' ); 252 | $this->send_common_stream_headers(); 253 | 254 | ob_clean(); 255 | flush(); 256 | readfile( $filename ); 257 | 258 | // fire action to allow stats logging 259 | do_action( $this->plugin_name . '-image-view', array( 'post_id' => $id, 'width' => $width, 'height' => $height, 'bytes' => $filesize ) ); 260 | die(); 261 | 262 | } 263 | else { 264 | $this->set_404(); 265 | } 266 | 267 | } 268 | 269 | 270 | private function send_image_stream_headers( $mime_type, $filesize, $filename ) { 271 | header( 'Content-Type: ' . sanitize_mime_type( $mime_type ) ); 272 | header( 'Content-Length: ' . intval( $filesize ) ); 273 | header( 'Content-Disposition: inline; filename=' . sanitize_file_name( $filename ) ); 274 | } 275 | 276 | 277 | private function send_common_stream_headers() { 278 | if ( $this->add_expires ) { 279 | $expires = DAY_IN_SECONDS * 15; 280 | header( 'Pragma: public' ); 281 | header( 'Cache-Control: public' ); 282 | header( 'Expires: ' . gmdate( 'D, d M Y H:i:s', time()+$expires ) . ' GMT' ); 283 | header( 'Last-Modified:Mon, 20 Aug 2012 19:20:21 GMT' ); 284 | } 285 | } 286 | 287 | 288 | private function add_image_size_meta( $width, $height ) { 289 | $image_size_name = $this->image_size_name( $width, $height ); 290 | 291 | if ( ! is_array( $this->sizes ) ) { 292 | $this->sizes = array(); 293 | } 294 | 295 | foreach ( $this->sizes as $s ) { 296 | if ( $s->name === $image_size_name ) 297 | return; 298 | } 299 | 300 | 301 | // size does not exist, add it 302 | $size = new stdClass(); 303 | $size->name = $image_size_name; 304 | $size->width = $width; 305 | $size->height = $height; 306 | 307 | $this->sizes[] = $size; 308 | 309 | add_image_size( $image_size_name, $width, $height, true ); 310 | 311 | } 312 | 313 | 314 | private function cached_file_exists( $metadata, $image_size_name, $width ) { 315 | if ( ! empty( $metadata ) && ! empty( $metadata[ $this->meta_sizes ] ) && ! empty( $metadata[ $this->meta_sizes ][ $image_size_name ] ) && ! empty( $metadata[ $this->meta_sizes ][ $image_size_name ]['file'] ) ) { 316 | return file_exists( $this->get_cached_file_path( $metadata[ $this->meta_sizes ][ $image_size_name ]['file'], $width ) ); 317 | } 318 | else { 319 | return false; 320 | } 321 | } 322 | 323 | 324 | /* 325 | if ( false ) { 326 | // this will force the resizer to conform to the dimensions of the original image 327 | // we may need to add code here to scale up the original image before resizing/cropping 328 | if ( ! empty( $metadata['width'] ) && $width > $metadata['width'] ) { 329 | $width = $metadata['width']; 330 | } 331 | 332 | if ( ! empty( $metadata['height'] ) && $width > $metadata['height'] ) { 333 | $height = $metadata['height']; 334 | } 335 | } 336 | */ 337 | 338 | 339 | private function generate_image( $id, $width, $height ) { 340 | $image_size_name = $this->image_size_name( $width, $height ); 341 | $this->add_image_size_meta( $width, $height ); 342 | $image = get_post( $id ); 343 | $image_size_exists = $this->image_size_exists( $id, $width, $height ); 344 | $metadata = wp_get_attachment_metadata( $id ); 345 | $cached_file_exists = $this->cached_file_exists( $metadata, $image_size_name, $width ); 346 | 347 | if ( ! empty( $image) && ( ! $image_size_exists || ! $cached_file_exists ) ) { 348 | $fullsizepath = get_attached_file( $image->ID ); 349 | include_once ABSPATH . 'wp-admin/includes/image.php'; 350 | 351 | if ( ! $cached_file_exists ) { 352 | $resized = $this->generate_resized_image( $fullsizepath, $width, $height ); 353 | } 354 | 355 | $this->update_image_metadata( $id, $resized, $metadata, $image_size_name ); 356 | } 357 | 358 | $this->add_image_size_association( $id, $width, $height ); 359 | 360 | return true; 361 | 362 | } 363 | 364 | 365 | private function update_image_metadata( $id, $resized, $metadata, $image_size_name ) { 366 | if ( ! empty( $resized ) && ! empty( $resized['file'] ) ) { 367 | $metadata[ $this->meta_sizes ][ $image_size_name ] = $resized; 368 | wp_update_attachment_metadata( $id, $metadata ); 369 | } 370 | } 371 | 372 | 373 | private function add_image_size_association( $id, $width, $height ) { 374 | // save option so we know which image to use for the size 375 | $exists = $this->get_existing_image_id_by_dimensions( $width, $height ); 376 | if ( empty( $exists ) ) { 377 | add_option( "_gga-placeholder-image-for-{$width}-{$height}", $id, '', 'no' ); 378 | } 379 | } 380 | 381 | 382 | private function generate_resized_image( $fullsizepath, $width, $height ) { 383 | // don't call wp_generate_attachment_metadata because it regenerates existing images 384 | $resized = image_make_intermediate_size( $fullsizepath, $width, $height, $crop=true ); 385 | if ( ! empty( $resized ) ) { 386 | $this->move_resized_to_cache( $resized['file'], $fullsizepath, $width ); 387 | return $resized; 388 | } else { 389 | return false; 390 | } 391 | 392 | } 393 | 394 | 395 | private function move_resized_to_cache( $resized_filename, $fullsizepath, $width ) { 396 | delete_site_transient( $this->plugin_name . '-cache-size' ); 397 | rename( path_join( dirname($fullsizepath), $resized_filename ), path_join( $this->get_cache_directory_for_width( $width ), $resized_filename ) ); 398 | } 399 | 400 | 401 | private function get_existing_image_id_by_dimensions( $width, $height ) { 402 | $id = get_option( "_gga-placeholder-image-for-{$width}-{$height}", false ); 403 | return $id; 404 | } 405 | 406 | 407 | private function image_size_exists( $id, $width, $height ) { 408 | $meta = wp_get_attachment_metadata( $id ); 409 | return ! empty( $meta[ $this->meta_sizes ] ) && ! empty( $meta[ $this->meta_sizes ][$this->image_size_name( $width, $height )] ); 410 | } 411 | 412 | 413 | private function image_size_name( $width, $height ) { 414 | return 'gga-image-' . $width . '-' . $height; 415 | } 416 | 417 | 418 | private function get_cache_directory() { 419 | return apply_filters( $this->plugin_name . '-get-cache-directory', 'gga-dynamic-placeholder-images' ); 420 | } 421 | 422 | 423 | private function get_cache_directory_for_width( $width ) { 424 | $max_width = $this->get_max_width(); 425 | if ( $width > $max_width ) { 426 | $width = $max_width; 427 | } 428 | 429 | $width_directory = strval( floor( $width / 100 ) * 100 ); 430 | if ( $width_directory === '0' ) 431 | $width_directory = '1'; 432 | 433 | $width_directory = path_join( $this->get_cache_directory(), $width_directory ); 434 | if ( ! is_dir( $width_directory ) ) { 435 | wp_mkdir_p( $width_directory ); 436 | } 437 | 438 | return $width_directory; 439 | } 440 | 441 | 442 | private function get_cached_file_path( $filename, $width ) { 443 | return path_join( $this->get_cache_directory_for_width( $width ), $filename ); 444 | } 445 | 446 | 447 | private function get_max_width() { 448 | return intval( apply_filters( $this->plugin_name . '-setting-get', 2000, $this->plugin_name . '-settings-general', 'max-width' ) ); 449 | } 450 | 451 | 452 | private function get_max_height() { 453 | return intval( apply_filters( $this->plugin_name . '-setting-get', 2000, $this->plugin_name . '-settings-general', 'max-height' ) ); 454 | } 455 | 456 | 457 | // because Sneek read the code 458 | 459 | } 460 | 461 | 462 | } 463 | --------------------------------------------------------------------------------