├── .editorconfig
├── .gitignore
├── .travis.yml
├── Gruntfile.js
├── README.md
├── assets
└── js
│ └── image-shortcake-admin.js
├── bin
└── install-wp-tests.sh
├── composer.json
├── image-shortcake.php
├── inc
├── class-img-shortcode-data-migration.php
├── class-img-shortcode.php
└── class-wp-cli-img-shortcode-command.php
├── languages
├── image-shortcake-ru_RU.mo
├── image-shortcake-ru_RU.po
└── image-shortcake.pot
├── package.json
├── phpcs.ruleset.xml
├── phpunit.xml
├── readme.txt
├── screenshot-1.png
├── screenshot-2.png
└── tests
├── bootstrap.php
├── data
├── fusion_image_placeholder_16x9_h2000.png
└── gin_joints.wav
├── test-img-shortcode-data-migration.php
└── test-img-shortcode.php
/.editorconfig:
--------------------------------------------------------------------------------
1 | # This file is for unifying the coding style for different editors and IDEs
2 | # editorconfig.org
3 |
4 | # WordPress Coding Standards
5 | # http://make.wordpress.org/core/handbook/coding-standards/
6 |
7 | root = true
8 |
9 | [*]
10 | charset = utf-8
11 | end_of_line = lf
12 | insert_final_newline = true
13 | trim_trailing_whitespace = true
14 | indent_style = tab
15 | indent_size = 4
16 |
17 | [readme.txt,*.md,*.markdown]
18 | trim_trailing_whitespace = false
19 |
20 | [js-tests/**/*.js]
21 | indent_style = space
22 | indent_size = 2
23 |
24 | [*.json]
25 | indent_style = space
26 | indent_size = 2
27 |
28 | [*.txt,wp-config-sample.php]
29 | end_of_line = crlf
30 |
31 | [.scss-lint.yml]
32 | indent_style = space
33 | indent_size = 2
34 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .svn
2 | /node_modules
3 | /vendor
4 | .DS_Store
5 | .sass-cache
6 | composer.lock
7 | _SpecRunner.html
8 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 |
3 | language: php
4 |
5 | matrix:
6 | include:
7 | - php: 5.6
8 | env: WP_VERSION=latest WP_MULTISITE=1
9 | - php: 7.1
10 | env: WP_VERSION=latest WP_MULTISITE=0
11 |
12 | cache:
13 | directories:
14 | - vendor
15 | - node_modules
16 |
17 | before_script:
18 | - composer install
19 | - npm install
20 | - bash bin/install-wp-tests.sh wordpress_test root '' localhost $WP_VERSION
21 |
22 | script:
23 | - grunt phpcs
24 | - phpunit
25 |
26 | branches:
27 | only:
28 | - master
29 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | module.exports = function( grunt ) {
2 |
3 | 'use strict';
4 | var banner = '/**\n * <%= pkg.homepage %>\n * Copyright (c) <%= grunt.template.today("yyyy") %>\n * This file is generated automatically. Do not edit.\n */\n';
5 | // Project configuration
6 | grunt.initConfig( {
7 |
8 | pkg: grunt.file.readJSON( 'package.json' ),
9 |
10 | addtextdomain: {
11 | options: {
12 | textdomain: 'image-shortcake',
13 | },
14 | target: {
15 | files: {
16 | src: [ '*.php', '**/*.php', '!node_modules/**', '!php-tests/**', '!bin/**' ]
17 | }
18 | }
19 | },
20 |
21 | wp_readme_to_markdown: {
22 | your_target: {
23 | files: {
24 | 'README.md': 'readme.txt'
25 | }
26 | },
27 | },
28 |
29 | phpcs: {
30 | plugin: {
31 | src: './'
32 | },
33 | options: {
34 | bin: "vendor/bin/phpcs --extensions=php --ignore=\"*/vendor/*,*/node_modules/*\"",
35 | standard: "phpcs.ruleset.xml"
36 | }
37 | },
38 |
39 | watch: {
40 | dev: {
41 | files: [ '**/*.php' ],
42 | tasks: [ 'phpcs' ]
43 | }
44 | },
45 |
46 | makepot: {
47 | target: {
48 | options: {
49 | domainPath: '/languages',
50 | mainFile: 'image-shortcake.php',
51 | potFilename: 'image-shortcake.pot',
52 | potHeaders: {
53 | poedit: true,
54 | 'x-poedit-keywordslist': true
55 | },
56 | type: 'wp-plugin',
57 | updateTimestamp: true
58 | }
59 | }
60 | },
61 | } );
62 |
63 | grunt.loadNpmTasks( 'grunt-wp-i18n' );
64 | grunt.loadNpmTasks( 'grunt-wp-readme-to-markdown' );
65 | grunt.loadNpmTasks( 'grunt-phpcs' );
66 | grunt.loadNpmTasks( 'grunt-contrib-watch' );
67 | grunt.registerTask( 'i18n', ['addtextdomain', 'makepot'] );
68 | grunt.registerTask( 'readme', ['wp_readme_to_markdown']);
69 |
70 |
71 | grunt.util.linefeed = '\n';
72 |
73 | };
74 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Image Shortcake #
2 | **Contributors:** fusionengineering, goldenapples, danielbachhuber
3 | **Tags:** shortcodes, images
4 | **Requires at least:** 3.0.1
5 | **Tested up to:** 4.3
6 | **Stable tag:** 0.1.0
7 | **License:** GPLv2 or later
8 | **License URI:** http://www.gnu.org/licenses/gpl-2.0.html
9 |
10 | Image Shortcake adds a shortcode for images, so that themes can template and filter images displayed in posts. Although it can be used standalone, it is designed to work with the UI provided by the [Shortcake (Shortcode UI)](https://github.com/fusioneng/Shortcake) plugin.
11 |
12 | ## Description ##
13 |
14 | When images are inserted into posts from the media library or media uploader, only the html of the ` ` tag and the link around it (if any) are preserved. This means that themes which want to change the way images are marked up in content don't have an easy way of doing this.
15 |
16 | Image Shortcake is an attempt to solve this problem, by saving images in post content as _shortcodes_ rather than HTML. The output of shortcodes can be easily filtered in themes, plugins and templates, and since the original attachment data is preseved as attributes on the shortcode, it becomes much easier for modify the way images are marked up in themes.
17 |
18 | For best results, use this with the [Shortcake (Shortcode UI)](https://github.com/fusioneng/Shortcake) plugin. Shortcake offers an easy to use interface to manage shortcodes in post content.
19 |
20 | What could you use this for? Well, at [Fusion](http://fusion.net) we use this shortcode for:
21 |
22 | * **Responsive Images**. By filtering the output of the `[img]` shortcode image tag, we're able to insert the `srcset` attribute, so that all of the images on our site are served responsively to browsers that support that.
23 |
24 | * **Inline sharing buttons**. We've added share links to each of the images on our site. Because these are inserted through a filter on a shortcode and not in the post content, it's easy to modify them on the fly. And having this logic in template files rather in on-page javascript that runs after page load makes it quicker for users.
25 |
26 | * **Photo credits**. We've added "credit" as an image meta field, and we use a filter on 'img_shortcode_output_after_linkify' to display it on all images.
27 |
28 | See the [Installation](#Installation) section for more ideas and tips for custom image templates. [Get involved with the project](https://github.com/fusioneng/image-shortcake) on Github.
29 |
30 | ## Installation ##
31 |
32 | ### Customizing Output ###
33 |
34 | The whole point of using shortcodes rather than HTML tags for images is that you can customize the markup of images in your theme. This plugin offers three primary hooks to modify the output:
35 |
36 | * `img_shortcode_output_img_tag`: Filters the output of the tag before wrapping it in link or caption
37 | * `img_shortcode_output_after_linkify`: Filters the output of the tag after wrapping in link
38 | * `img_shortcode_output_after_captionify`: Filters the output of the tag after wrapping in link and attaching caption
39 |
40 | You can, of course, disregard the markup generated by this plugin altogether and use a template part for images if you want. This example adds EXIF data below all images containing those fields, in all post content:
41 |
42 | in the theme's `functions.php`:
43 |
44 | ```
45 | add_filter( 'img_shortcode_output_img_tag', 'load_image_template', 10, 2 );
46 |
47 | function load_image_template( $_, $attributes ) {
48 | ob_start();
49 | get_template_part( 'inline-image' );
50 | return ob_get_clean();
51 | }
52 | ```
53 |
54 | in a template file called `inline-image.php`:
55 |
56 | ```
57 | "$class $align attachment-$size",
72 | 'alt' => $alt,
73 | )
74 | );
75 |
76 | if ( is_array( $exif_data ) ) {
77 | echo '
';
82 | }
83 | ```
84 |
85 |
86 | ### Data Migration ###
87 |
88 | The plugin comes with two [WP-CLI](http://wp-cli.org) commands to migrate images in your existing content into the `[img]` shortcode format used by
89 | **this plugin. _Note:** if it isn't clear, this is an early release -- use at your own risk, and make sure you've backed up your posts before migrating!_
90 |
91 | `wp image-shortcake migrate [--dry-run]`
92 |
93 | This command searches the post content of the posts specified in ``, and replaces any ` ` tags or `[caption]` shortcodes with `[img]`
94 | shortcodes. Currently it only catches images added through the media library; custom img tags will not be converted.
95 |
96 | If you add the `--dry-run` flag, no replacements will actually be performed, just a summary report of the changes which would have been made.
97 |
98 | `wp image-shortcake revert [--dry-run]`
99 |
100 | This command finds all `[img]` shortcodes in the content of any of the posts specified in ``, and replaces them with the markup that would be generated by those shortcodes. Note that this runs any filters in your theme, so that if you have filtered the output of the shortcodes at any output, those filters will be reflected in the coverted post content.
101 |
102 | ## Screenshots ##
103 |
104 | ### 1. This is the shortcode UI form as accessed from **Insert Media > Insert Post Element**. (Note that you can also insert images as usual, by inserting them in the Media Library - they will be transparently converted into shortcodes behind the scenes for you.) ###
105 | 
106 |
107 | ### 2. Once inserted into a post, the image preview renders in the editor just as it normally would. The Shortcake plugin's edit/delete buttons are available to modify the shortcode through the provided UI. ###
108 | 
109 |
110 |
111 | ## Changelog ##
112 |
113 | ### 0.1.0 (May 1, 2015) ###
114 | * Initial release
115 |
--------------------------------------------------------------------------------
/assets/js/image-shortcake-admin.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Callback to manipulate the attachment details and two column template, onReady.
3 | */
4 | function updateStringsForImageShortcake() {
5 | var attachmentDetailsTemplate = jQuery("#tmpl-attachment-details");
6 |
7 | if ( 0 === attachmentDetailsTemplate.length ) {
8 | return;
9 | }
10 |
11 | var attachmentDetailsHtml = attachmentDetailsTemplate.html(),
12 | attachmentDetailsTwoColumnTemplate = jQuery("#tmpl-attachment-details-two-column"),
13 | attachmentDetailsTwoColumnHtml = attachmentDetailsTwoColumnTemplate.html(),
14 |
15 | customCaption = '<# if( "image" === data.type ) { #>' +
16 | '' +
17 | image_shortcake_strings.caption +
18 | ' ' +
19 | '<# } #>' +
20 | '' +
21 | '' +
22 | image_shortcake_strings.warning +
23 | '.
' +
24 | '';
25 |
26 | /**
27 | * Use string methods hack.
28 | */
29 | var newHtml = attachmentDetailsHtml.replace(/()[\w\W]*?(<\/label>)/,"$1 " + customCaption + " $2");
30 | newHtml = newHtml.replace(/([\w\W]*?<\/label>)/,"<# if( 'image' === data.type ) { #>$1<# } #>");
31 | attachmentDetailsTemplate.text( newHtml );
32 |
33 | newHtml = attachmentDetailsTwoColumnHtml.replace(/()[\w\W]*?(<\/label>)/,"$1 " + customCaption + " $2");
34 | newHtml = newHtml.replace(/([\w\W]*?<\/label>)/,"<# if( 'image' === data.type ) { #>$1<# } #>");
35 | attachmentDetailsTwoColumnTemplate.text( newHtml );
36 | };
37 |
38 | jQuery(document).ready(function(){
39 | updateStringsForImageShortcake();
40 | });
41 |
42 | var ImageShortcake = {
43 |
44 | listeners: {
45 | /**
46 | * Callback for the "attachment" attribute field.
47 | *
48 | * When selecting an attachment for an [img], try to populate image
49 | * shortcode attribute fields (alt, caption, description,etc.) from
50 | * the attachment data.
51 | */
52 | attachment: function( changed, collection, shortcode ) {
53 | if ( typeof changed.value === 'undefined' ) {
54 | return;
55 | }
56 |
57 | var attachment = sui.views.editAttributeFieldAttachment.getFromCache( changed.value );
58 |
59 | if ( attachment ) {
60 |
61 | var attrView = sui.views.editAttributeField,
62 | altField = attrView.getField( collection, 'alt' ),
63 | captionField = attrView.getField( collection, 'caption' );
64 |
65 | if ( ! altField.getValue() && attachment.alt ) {
66 | altField.$el.find('[name="alt"]').val( attachment.alt );
67 | }
68 |
69 | if ( ! captionField.getValue() && attachment.caption ) {
70 | captionField.$el.find('[name="caption"]').val( attachment.caption );
71 | }
72 | }
73 | },
74 |
75 | /**
76 | * Callback for the "linkto" attribute field.
77 | *
78 | * Display the "Custom Link" field if and only if the "linkto" field is "custom"
79 | */
80 | linkto: function( changed, collection, shortcode ) {
81 | var customLinkField = sui.views.editAttributeField.getField( collection, 'url' );
82 |
83 | if ( changed.value === 'custom' ) {
84 | customLinkField.$el.show()
85 | } else {
86 | customLinkField.$el.val('').hide();
87 | }
88 | }
89 | }
90 |
91 | }
92 |
93 | /**
94 | * If using a recent enough version of Shortcake (0.4.0 and up),
95 | * attach these listeners to the attributes.
96 | *
97 | */
98 | if ( typeof wp.shortcake !== 'undefined' && typeof wp.shortcake.hooks !== 'undefined' ) {
99 |
100 | wp.shortcake.hooks.addAction( 'img.attachment', ImageShortcake.listeners.attachment );
101 | wp.shortcake.hooks.addAction( 'img.linkto', ImageShortcake.listeners.linkto );
102 |
103 | }
104 |
--------------------------------------------------------------------------------
/bin/install-wp-tests.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | if [ $# -lt 3 ]; then
4 | echo "usage: $0 [db-host] [wp-version]"
5 | exit 1
6 | fi
7 |
8 | DB_NAME=$1
9 | DB_USER=$2
10 | DB_PASS=$3
11 | DB_HOST=${4-localhost}
12 | WP_VERSION=${5-latest}
13 |
14 | WP_TESTS_DIR=${WP_TESTS_DIR-/tmp/wordpress-tests-lib}
15 | WP_CORE_DIR=${WP_CORE_DIR-/tmp/wordpress/}
16 |
17 | download() {
18 | if [ `which curl` ]; then
19 | curl -s "$1" > "$2";
20 | elif [ `which wget` ]; then
21 | wget -nv -O "$2" "$1"
22 | fi
23 | }
24 |
25 | if [[ $WP_VERSION =~ [0-9]+\.[0-9]+(\.[0-9]+)? ]]; then
26 | WP_TESTS_TAG="tags/$WP_VERSION"
27 | else
28 | # http serves a single offer, whereas https serves multiple. we only want one
29 | download http://api.wordpress.org/core/version-check/1.7/ /tmp/wp-latest.json
30 | grep '[0-9]+\.[0-9]+(\.[0-9]+)?' /tmp/wp-latest.json
31 | LATEST_VERSION=$(grep -o '"version":"[^"]*' /tmp/wp-latest.json | sed 's/"version":"//')
32 | if [[ -z "$LATEST_VERSION" ]]; then
33 | echo "Latest WordPress version could not be found"
34 | exit 1
35 | fi
36 | WP_TESTS_TAG="tags/$LATEST_VERSION"
37 | fi
38 |
39 | set -ex
40 |
41 | install_wp() {
42 |
43 | if [ -d $WP_CORE_DIR ]; then
44 | return;
45 | fi
46 |
47 | mkdir -p $WP_CORE_DIR
48 |
49 | if [ $WP_VERSION == 'latest' ]; then
50 | local ARCHIVE_NAME='latest'
51 | else
52 | local ARCHIVE_NAME="wordpress-$WP_VERSION"
53 | fi
54 |
55 | download https://wordpress.org/${ARCHIVE_NAME}.tar.gz /tmp/wordpress.tar.gz
56 | tar --strip-components=1 -zxmf /tmp/wordpress.tar.gz -C $WP_CORE_DIR
57 |
58 | download https://raw.github.com/markoheijnen/wp-mysqli/master/db.php $WP_CORE_DIR/wp-content/db.php
59 | }
60 |
61 | install_test_suite() {
62 | # portable in-place argument for both GNU sed and Mac OSX sed
63 | if [[ $(uname -s) == 'Darwin' ]]; then
64 | local ioption='-i .bak'
65 | else
66 | local ioption='-i'
67 | fi
68 |
69 | # set up testing suite if it doesn't yet exist
70 | if [ ! -d $WP_TESTS_DIR ]; then
71 | # set up testing suite
72 | mkdir -p $WP_TESTS_DIR
73 | svn co --quiet https://develop.svn.wordpress.org/${WP_TESTS_TAG}/tests/phpunit/includes/ $WP_TESTS_DIR/includes
74 | fi
75 |
76 | cd $WP_TESTS_DIR
77 |
78 | if [ ! -f wp-tests-config.php ]; then
79 | download https://develop.svn.wordpress.org/${WP_TESTS_TAG}/wp-tests-config-sample.php "$WP_TESTS_DIR"/wp-tests-config.php
80 | sed $ioption "s:dirname( __FILE__ ) . '/src/':'$WP_CORE_DIR':" "$WP_TESTS_DIR"/wp-tests-config.php
81 | sed $ioption "s/youremptytestdbnamehere/$DB_NAME/" "$WP_TESTS_DIR"/wp-tests-config.php
82 | sed $ioption "s/yourusernamehere/$DB_USER/" "$WP_TESTS_DIR"/wp-tests-config.php
83 | sed $ioption "s/yourpasswordhere/$DB_PASS/" "$WP_TESTS_DIR"/wp-tests-config.php
84 | sed $ioption "s|localhost|${DB_HOST}|" "$WP_TESTS_DIR"/wp-tests-config.php
85 | fi
86 |
87 | }
88 |
89 | install_db() {
90 | # parse DB_HOST for port or socket references
91 | local PARTS=(${DB_HOST//\:/ })
92 | local DB_HOSTNAME=${PARTS[0]};
93 | local DB_SOCK_OR_PORT=${PARTS[1]};
94 | local EXTRA=""
95 |
96 | if ! [ -z $DB_HOSTNAME ] ; then
97 | if [ $(echo $DB_SOCK_OR_PORT | grep -e '^[0-9]\{1,\}$') ]; then
98 | EXTRA=" --host=$DB_HOSTNAME --port=$DB_SOCK_OR_PORT --protocol=tcp"
99 | elif ! [ -z $DB_SOCK_OR_PORT ] ; then
100 | EXTRA=" --socket=$DB_SOCK_OR_PORT"
101 | elif ! [ -z $DB_HOSTNAME ] ; then
102 | EXTRA=" --host=$DB_HOSTNAME --protocol=tcp"
103 | fi
104 | fi
105 |
106 | # create database
107 | mysqladmin create $DB_NAME --user="$DB_USER" --password="$DB_PASS"$EXTRA
108 | }
109 |
110 | install_wp
111 | install_test_suite
112 | install_db
113 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fusioneng/image-shortcake",
3 | "authors": [
4 | {
5 | "name": "Fusion Engineering",
6 | "email": "tech@fusion.net"
7 | }
8 | ],
9 | "require-dev": {
10 | "wp-coding-standards/wpcs": "dev-develop"
11 | },
12 | "scripts": {
13 | "post-install-cmd": "\"vendor/bin/phpcs\" --config-set installed_paths vendor/wp-coding-standards/wpcs",
14 | "post-update-cmd" : "\"vendor/bin/phpcs\" --config-set installed_paths vendor/wp-coding-standards/wpcs"
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/image-shortcake.php:
--------------------------------------------------------------------------------
1 | load_textdomain();
30 | self::$instance->register_shortcode();
31 | self::$instance->setup_filters();
32 | self::$instance->enqueue_assets();
33 | }
34 |
35 | return self::$instance;
36 | }
37 |
38 | /**
39 | * Load translations
40 | *
41 | * @return void
42 | */
43 | private static function load_textdomain() {
44 | load_plugin_textdomain( 'image-shortcake', false,
45 | dirname( plugin_basename( __FILE__ ) ) . '/languages/'
46 | );
47 | }
48 |
49 | /**
50 | * Require the plugin's shortcode class file.
51 | *
52 | */
53 | private static function require_files() {
54 | require_once( dirname( __FILE__ ) . '/inc/class-img-shortcode.php' );
55 |
56 | require_once( dirname( __FILE__ ) . '/inc/class-img-shortcode-data-migration.php' );
57 |
58 | if ( defined( 'WP_CLI' ) && WP_CLI ) {
59 | require_once( dirname( __FILE__ ) . '/inc/class-wp-cli-img-shortcode-command.php' );
60 | }
61 | }
62 |
63 |
64 | /**
65 | * Register the [img] shortcode and the UI for it..
66 | *
67 | */
68 | private function register_shortcode() {
69 |
70 | add_shortcode( 'img', 'Img_Shortcode::callback' );
71 |
72 | if ( function_exists( 'shortcode_ui_register_for_shortcode' ) ) {
73 | shortcode_ui_register_for_shortcode( 'img', Img_Shortcode::get_shortcode_ui_args() );
74 | } else {
75 | add_action( 'admin_notices', array( $this, 'action_admin_notices_warning' ) );
76 | }
77 | }
78 |
79 |
80 | /**
81 | * Set up filters to integrate this shortcode with the media library output.
82 | *
83 | */
84 | private function setup_filters() {
85 | add_filter( 'media_send_to_editor', 'Img_Shortcode::filter_media_send_to_editor', 15, 3 );
86 | }
87 |
88 |
89 | /**
90 | * Enqueue scripts and styles for editor admin area
91 | *
92 | * Defines the callback function which is run on chanes to any of the
93 | * shortcode attributes through the UI.
94 | */
95 | public function enqueue_assets() {
96 | add_action( 'enqueue_shortcode_ui', array( $this, 'action_enqueue_shortcode_ui' ) );
97 | }
98 |
99 |
100 | /**
101 | * Enqueues the attribute event handler functions on edit page
102 | * Adds some localized text
103 | *
104 | */
105 | public function action_enqueue_shortcode_ui() {
106 | wp_enqueue_script( 'image-shortcake-admin', plugin_dir_url( __FILE__ ) . 'assets/js/image-shortcake-admin.js', false, IMAGE_SHORTCAKE_VERSION );
107 | $translation_array = array(
108 | 'caption' => __( 'Caption', 'image-shortcake' ),
109 | 'warning' => __( 'Double quotes and HTML tags are not allowed in captions', 'image-shortcake' ),
110 | );
111 | wp_localize_script( 'image-shortcake-admin', 'image_shortcake_strings', $translation_array );
112 | }
113 |
114 |
115 | /**
116 | * Output a warning notice to authorized users if shortcake is not active.
117 | *
118 | * if Shortcode UI plugin is not active, the UI for the [img] shortcode
119 | * will not be able to be registered.
120 | *
121 | * @action admin_notices
122 | */
123 | public function action_admin_notices_warning() {
124 | if ( current_user_can( 'activate_plugins' ) ) {
125 | echo '' .
126 | esc_html__( 'Shortcode UI plugin is not active. No UI will be available for the image shortcode.', 'image-shortcake' ) .
127 | '
';
128 | }
129 | }
130 |
131 | }
132 |
133 | add_action( 'init', 'Image_Shortcake::get_instance' );
134 |
--------------------------------------------------------------------------------
/inc/class-img-shortcode-data-migration.php:
--------------------------------------------------------------------------------
1 | ]+' .
13 | 'href="(?P[^"]*)"' .
14 | '[^>]*>)?' .
15 | ' ]*' .
16 | 'class="' .
17 | '(?|size-(?P\w+))?' . ' ?' .
18 | '(?|wp-image-(?P\d+))?' . ' ?' .
19 | '(?|(?Palign[\w-]+))?' . ' ?' .
20 | '[^"]*" ' . // end of class attribute
21 | 'src="(?P[^"]*)" ?' .
22 | '(?:alt="(?P[^"]*)" ?)?' .
23 | '(?:width="(?P[^"]*)" ?)?' .
24 | '(?:height="(?P[^"]*)" ?)?' .
25 | '[^>]*>' .
26 | '(?:<\/a>)?';
27 |
28 | return $img_shortcode_regex;
29 | }
30 |
31 |
32 | private static function caption_shortcode_regex() {
33 | $caption_shortcode_regex =
34 | '\[caption' .
35 | '[^\]]*' . '\]\]?' .
36 | self::img_shortcode_regex() .
37 | '(?: (?P[^\]]*))' .
38 | '\[\[?\/caption\]\]?';
39 | return $caption_shortcode_regex;
40 | }
41 |
42 | public static function find_img_tags_for_replacement_on_post( $post ) {
43 | $post = self::maybe_get_post_from_id( $post );
44 | if ( ! $post ) {
45 | return false;
46 | }
47 |
48 | return self::find_img_tags_for_replacement( $post->post_content );
49 | }
50 |
51 | /**
52 | * Find all ` ` tags in a string that can be replaced.
53 | *
54 | * @param string String containing tags, for example post content
55 | * @return array Array of found tags => [img] replacements
56 | */
57 | public static function find_img_tags_for_replacement( $post_content ) {
58 |
59 | $replacements = array();
60 |
61 | $img_shortcode_regex = self::img_shortcode_regex();
62 |
63 | preg_match_all(
64 | "/$img_shortcode_regex/s",
65 | $post_content,
66 | $matches,
67 | PREG_SET_ORDER
68 | );
69 |
70 | if ( 0 === count( $matches ) ) {
71 | return array();
72 | }
73 |
74 | foreach ( $matches as $matched_pattern ) {
75 | $replacements[ $matched_pattern[0] ] = self::convert_img_tag_to_shortcode(
76 | $matched_pattern[0],
77 | $matched_pattern
78 | );
79 | }
80 |
81 | return $replacements;
82 | }
83 |
84 |
85 | public static function find_caption_shortcodes_for_replacement_on_post( $post ) {
86 | $post = self::maybe_get_post_from_id( $post );
87 | if ( ! $post ) {
88 | return false;
89 | }
90 |
91 | return self::find_caption_shortcodes_for_replacement( $post->post_content );
92 | }
93 |
94 | /**
95 | * Find all [caption] shortcodes in a string that can be replaced with [img] shortcodes.
96 | *
97 | * @param string String containing tags, for example post content
98 | * @return array Array of found caption shortcodes => [img] replacements
99 | */
100 | public static function find_caption_shortcodes_for_replacement( $post_content ) {
101 |
102 | $replacements = array();
103 |
104 | $caption_shortcode_regex = self::caption_shortcode_regex();
105 |
106 | preg_match_all(
107 | "/$caption_shortcode_regex/s",
108 | $post_content,
109 | $matches,
110 | PREG_SET_ORDER
111 | );
112 |
113 | if ( 0 === count( $matches ) ) {
114 | return array();
115 | }
116 |
117 | foreach ( $matches as $matched_pattern ) {
118 | $replacements[ $matched_pattern[0] ] = self::convert_img_tag_to_shortcode(
119 | $matched_pattern[0],
120 | $matched_pattern
121 | );
122 | }
123 |
124 | return $replacements;
125 | }
126 |
127 |
128 | /**
129 | * Convert an tag to its shortcode equivalent.
130 | *
131 | * @param string HTML ` ` element
132 | * @return string An `[img]` shortcode element.
133 | */
134 | public static function convert_img_tag_to_shortcode( $img_tag, $attributes ) {
135 |
136 | // Whitelist a few attributes that we can take in as they are
137 | $shortcode_attrs = array_intersect_key( $attributes,
138 | array(
139 | 'size' => null,
140 | 'attachment' => null,
141 | 'align' => null,
142 | 'alt' => null,
143 | 'caption' => null,
144 | 'width' => null,
145 | 'height' => null,
146 | )
147 | );
148 |
149 | // If this isn't a WP attachment, we'll just use its existing src attribute
150 | if ( empty( $shortcode_attrs['attachment'] ) ) {
151 | $shortcode_attrs['src'] = esc_url( $attributes['src'] );
152 | }
153 |
154 | // If there's a link, check whether its a link to file, attachment, or custom
155 | if ( ! empty( $attributes['href'] ) ) {
156 |
157 | if ( ! empty( $shortcode_attrs['attachment'] ) ) {
158 | $attachment_src = wp_get_attachment_image_src( $attributes['attachment'], 'full' );
159 |
160 | if ( get_permalink( (int) $attributes['attachment'] ) === $attributes['href'] ) {
161 | $shortcode_attrs['linkto'] = 'attachment';
162 | } elseif ( $attachment_src[0] === $attributes['href'] // link to full size image
163 | || $attributes['src'] === $attributes['href'] // link the same as image src
164 | ) {
165 | $shortcode_attrs['linkto'] = 'file';
166 | } else {
167 | $shortcode_attrs['href'] = $attributes['href'];
168 | }
169 | } else {
170 | $shortcode_attrs['href'] = $attributes['href'];
171 | }
172 | }
173 |
174 | $shortcode_attrs = array_filter( $shortcode_attrs );
175 |
176 | $shortcode = '[img ';
177 |
178 | foreach ( $shortcode_attrs as $attr_name => $attr_value ) {
179 | $shortcode .= sanitize_key( $attr_name ) . '="' . esc_attr( $attr_value ) . '" ';
180 | }
181 |
182 | $shortcode .= '/]';
183 |
184 | return $shortcode;
185 | }
186 |
187 |
188 | /**
189 | * Get a post from a Post ID or post object.
190 | *
191 | * Simple utility function which allows the other functions in this class
192 | * to accept either Post IDs or Post objects as arguments.
193 | *
194 | * @param int|WP_Post $post
195 | * @return false|WP_Post Post object, or false if one can't be found.
196 | */
197 | private static function maybe_get_post_from_id( $post ) {
198 |
199 | if ( is_int( $post ) ) {
200 | $post = get_post( $post );
201 | }
202 |
203 | return ( $post instanceof WP_Post ) ? $post : false;
204 | }
205 |
206 | }
207 |
--------------------------------------------------------------------------------
/inc/class-img-shortcode.php:
--------------------------------------------------------------------------------
1 | esc_html__( 'Thumbnail', 'image-shortcake' ),
23 | 'medium' => esc_html__( 'Medium', 'image-shortcake' ),
24 | 'large' => esc_html__( 'Large', 'image-shortcake' ),
25 | 'full' => esc_html__( 'Full size', 'image-shortcake' ),
26 | );
27 | $sizes_available = array();
28 |
29 | foreach ( apply_filters( 'image_size_names_choose', $default_sizes ) as $key => $name ) {
30 |
31 | $size = get_option( "${key}_size_w" );
32 |
33 | if ( false === $size ) {
34 | if ( array_key_exists( $key, $_wp_additional_image_sizes ) ) {
35 | $size = $_wp_additional_image_sizes[ $key ]['width'];
36 | }
37 | }
38 |
39 | $size_str = $size ? " ({$size}px)" : '';
40 |
41 | $sizes_available[ $key ] = esc_attr( "{$name}{$size_str}" );
42 | }
43 |
44 | $shortcode_ui_args = array(
45 |
46 | 'label' => esc_html__( 'Image', 'image-shortcake' ),
47 |
48 | 'listItemImage' => 'dashicons-format-image',
49 |
50 | 'attrs' => array(
51 |
52 | array(
53 | 'label' => esc_html__( 'Choose Attachment', 'image-shortcake' ),
54 | 'attr' => 'attachment',
55 | 'type' => 'attachment',
56 | 'libraryType' => array( 'image' ),
57 | 'addButton' => esc_attr__( 'Select Image', 'image-shortcake' ),
58 | 'frameTitle' => esc_attr__( 'Select Image', 'image-shortcake' ),
59 | ),
60 |
61 | array(
62 | 'label' => esc_html__( 'Image size', 'image-shortcake' ),
63 | 'attr' => 'size',
64 | 'type' => 'select',
65 | 'value' => 'large',
66 | 'options' => $sizes_available,
67 | ),
68 |
69 | array(
70 | 'label' => esc_html__( 'Alt', 'image-shortcake' ),
71 | 'attr' => 'alt',
72 | 'type' => 'text',
73 | 'encode' => true,
74 | 'placeholder' => esc_attr__( 'Alt text for the image', 'image-shortcake' ),
75 | ),
76 |
77 | array(
78 | 'label' => esc_html__( 'Caption', 'image-shortcake' ),
79 | 'attr' => 'caption',
80 | 'type' => 'text',
81 | 'encode' => true,
82 | 'placeholder' => esc_attr__( 'Caption for the image', 'image-shortcake' ),
83 | ),
84 |
85 | array(
86 | 'label' => esc_html__( 'Alignment', 'image-shortcake' ),
87 | 'attr' => 'align',
88 | 'type' => 'select',
89 | 'value' => 'aligncenter',
90 | 'options' => array(
91 | 'alignleft' => esc_attr__( 'Left', 'image-shortcake' ),
92 | 'aligncenter' => esc_attr__( 'Center', 'image-shortcake' ),
93 | 'alignright' => esc_attr__( 'Right', 'image-shortcake' ),
94 | 'alignnone' => esc_attr__( 'None', 'image-shortcake' ),
95 | ),
96 | ),
97 |
98 | array(
99 | 'label' => esc_html__( 'Link to', 'image-shortcake' ),
100 | 'attr' => 'linkto',
101 | 'type' => 'select',
102 | 'value' => get_option( 'image_default_link_type' ),
103 | 'options' => array(
104 | 'none' => esc_attr__( 'None (no link)', 'image-shortcake' ),
105 | 'attachment' => esc_attr__( 'Link to attachment file', 'image-shortcake' ),
106 | 'file' => esc_attr__( 'Link to file', 'image-shortcake' ),
107 | 'custom' => esc_attr__( 'Custom link', 'image-shortcake' ),
108 | ),
109 | ),
110 |
111 | array(
112 | 'label' => esc_html__( 'Custom link', 'image-shortcake' ),
113 | 'attr' => 'url',
114 | 'type' => 'text',
115 | 'placeholder' => esc_attr__( 'URL to link to (if above link is "custom")', 'image-shortcake' ),
116 | ),
117 | ),
118 | );
119 |
120 | /**
121 | * Filter the shortcode UI definition arguments
122 | *
123 | * @param array Shortcode UI arguments
124 | */
125 | $shortcode_ui_args = apply_filters( 'img_shortcode_ui_args', $shortcode_ui_args );
126 |
127 | return $shortcode_ui_args;
128 | }
129 |
130 |
131 | /**
132 | * Take content containing existing image tags or [caption] shortcodes,
133 | * turn it into the shortcodes we want, so all images can be processed in
134 | * the same way.
135 | *
136 | */
137 | public static function reversal( $content ) {
138 | /**
139 | * TODO: detect images in content. If we can, try and replace them with
140 | * the [img] shortcode.
141 | */
142 | return $content;
143 | }
144 |
145 |
146 | /**
147 | * Render output from this shortcode.
148 | *
149 | * Can be filtered at several different points.
150 | *
151 | * @param array $attrs Shortcode attributes. See definitions in
152 | * @function `get_shortcode_ui_args()`
153 | * @param (not used) Inner content argument
154 | * @param string $shortcode_tag
155 | * @return string
156 | */
157 | public static function callback( $attr, $_null, $shortcode_tag ) {
158 |
159 | // Get all registered args; shortcode_atts() needs a whitelist of args
160 | $shortcode_args = static::get_shortcode_ui_args();
161 | $registered_atts = array_fill_keys( wp_list_pluck( $shortcode_args['attrs'], 'attr' ), null );
162 | $args_with_defaults = array_merge( $registered_atts,
163 | array(
164 | 'src' => null,
165 | 'size' => 'full',
166 | 'classes' => '',
167 | 'align' => 'alignnone',
168 | )
169 | );
170 |
171 | $attr = shortcode_atts( $args_with_defaults, $attr, $shortcode_tag );
172 |
173 | /**
174 | * Filter the shortcode attributes before rendering
175 | *
176 | * @param array Shortcode attributes, decoded and merged with defaults.
177 | */
178 | $attr = apply_filters( 'img_shortcode_attrs', $attr );
179 |
180 | $image_html = ' $attr['alt'],
188 | 'class' => trim( implode( ' ', $image_classes ) ),
189 | );
190 |
191 | if ( isset( $attr['attachment'] ) &&
192 | $attachment = wp_get_attachment_image_src( (int) $attr['attachment'], $attr['size'] ) ) {
193 | $image_attr['src'] = esc_url( $attachment[0] );
194 | $image_attr['width'] = intval( $attachment[1] );
195 | $image_attr['height'] = intval( $attachment[2] );
196 | } elseif ( ! empty( $attr['src'] ) ) {
197 | $image_attr['src'] = esc_url( $attr['src'] );
198 | } else {
199 | return; // An image without a src isn't much of an image
200 | }
201 |
202 | foreach ( $image_attr as $attr_name => $attr_value ) {
203 | if ( ! empty( $attr_value ) ) {
204 | $image_html .= sanitize_key( $attr_name ) . '="' . esc_attr( $attr_value ) . '" ';
205 | }
206 | }
207 |
208 | $image_html .= '/>';
209 |
210 | /**
211 | * Filter the output of the tag before wrapping it in link or caption
212 | *
213 | * @param string HTML markup of the image tag
214 | * @param array Shortcode attributes
215 | */
216 | $image_html = apply_filters( 'img_shortcode_output_img_tag', $image_html, $attr );
217 |
218 | // If a link is specified, wrap the image in a link tag
219 | if ( ! empty( $attr['linkto'] ) &&
220 | ( in_array( $attr['linkto'], array( 'file', 'attachment' ), true ) ||
221 | ( 'custom' === $attr['linkto'] && ! empty( $attr['url'] ) ) ) ) {
222 | $image_html = self::linkify( $image_html, $attr );
223 | }
224 |
225 | /**
226 | * Filter the output of the tag after wrapping in link
227 | *
228 | * @param string HTML markup of the image tag, possibly wrapped in a link
229 | * @param array Shortcode attributes
230 | */
231 | $image_html = apply_filters( 'img_shortcode_output_after_linkify', $image_html, $attr );
232 |
233 | // If a caption is specified, wrap the image in the appropriat caption markup.
234 | if ( ! empty( $attr['caption'] ) ) {
235 |
236 | // The WP caption element requires a width defined
237 | if ( empty( $attr['width'] ) ) {
238 | $attr['width'] = $image_attr['width'];
239 | }
240 |
241 | $image_html = self::captionify( $image_html, $attr );
242 | }
243 |
244 | /**
245 | * Filter the output of the tag after wrapping in link and attaching caption
246 | *
247 | * @param string HTML markup of the image tag, possibly wrapped in a link and caption
248 | * @param array Shortcode attributes
249 | */
250 | $image_html = apply_filters( 'img_shortcode_output_after_captionify', $image_html, $attr );
251 |
252 | return $image_html;
253 | }
254 |
255 |
256 | /**
257 | * Wrap an image in a link, if required.
258 | *
259 | * Returns either the img tag passed in, if no link is specified, or the
260 | * img wrapped in a link if we know the link to build.
261 | *
262 | * @param string $img_tag string representing an HTML element
263 | * @param array $attributes Shortcode attributes from the [img] shortcode.
264 | * @return string HTML representing an `` element surrounding an image.
265 | */
266 | private static function linkify( $img_tag, $attributes ) {
267 |
268 | $_id = intval( $attributes['attachment'] );
269 |
270 | $link_attrs = array();
271 |
272 | if ( isset( $attributes['url'] ) ) {
273 | $link_attrs['href'] = esc_url( $attributes['url'] );
274 | } elseif ( ! empty( $attributes['linkto'] ) && 'attachment' === $attributes['linkto'] ) {
275 | $link_attrs['href'] = get_permalink( $_id );
276 | } elseif ( ! empty( $attributes['linkto'] ) && 'file' === $attributes['linkto'] ) {
277 | $attachment_src = wp_get_attachment_image_src( $_id, 'full', false, $attributes );
278 | $link_attrs['href'] = $attachment_src[0];
279 | } else {
280 | // No link is defined, or its in a format that's not implemented yet.
281 | return $img_tag;
282 | }
283 |
284 | $html = ' $attr_value ) {
287 | $html .= sanitize_key( $attr_name ) . '="' . esc_attr( $attr_value ) . '" ';
288 | }
289 |
290 | $html .= '>' . $img_tag . ' ';
291 |
292 | return $html;
293 | }
294 |
295 |
296 | /**
297 | * Wrap an image in the markup for a caption.
298 | *
299 | * Uses the `img_caption_shortcode` function from WP core for compatibility
300 | * with themes and plugins that already filter caption markup through filters there.
301 | *
302 | * @attr string $img_tag HTML markup for the tag.
303 | * @attr string $caption Caption text.
304 | * @attr array $attributes The attributes set on the shortcode.
305 | * @return string HTML `` element representing the image and caption
306 | */
307 | private static function captionify( $img_tag, $attributes ) {
308 |
309 | $attributes = wp_parse_args( $attributes,
310 | array(
311 | 'id' => null,
312 | 'caption' => '',
313 | 'title' => '',
314 | 'align' => '',
315 | 'url' => '',
316 | 'size' => '',
317 | 'width' => '',
318 | 'alt' => '',
319 | )
320 | );
321 |
322 | // Ensure the image has a width defined; caption shortcode will break otherwise.
323 | if ( 0 === intval( $attributes['width'] ) ) {
324 | $_attachment_src = wp_get_attachment_image_src( $attributes['id'], $attributes['size'] );
325 |
326 | if ( $_attachment_src ) {
327 | $attributes['width'] = $_attachment_src[1];
328 | }
329 | }
330 |
331 | $html = img_caption_shortcode( $attributes, $img_tag );
332 |
333 | return $html;
334 | }
335 |
336 |
337 | /**
338 | * Catch images inserted through the media library, and convert them to the
339 | * shortcode format introduced by this plugin.
340 | *
341 | * @filter media_send_to_editor
342 | * @param string $html Generated by `wp_ajax_send_attachment_to_editor()`
343 | * @param int $id Attachment ID
344 | * @param array $attachment Attributes selected in the media editor
345 | * @return string
346 | */
347 | public static function filter_media_send_to_editor( $html, $attachment_id, $attachment ) {
348 |
349 | $media_post = get_post( $attachment_id );
350 |
351 | if ( ! $media_post || 'image' !== strtolower( substr( $media_post->post_mime_type, 0, 5 ) ) ) {
352 | return $html;
353 | }
354 |
355 | $shortcode_attrs = array(
356 | 'attachment' => $media_post->ID,
357 | );
358 |
359 | if ( ! empty( $attachment['align'] ) ) {
360 | $shortcode_attrs['align'] = 'align' . $attachment['align'];
361 | }
362 |
363 | $allowed_attrs = array(
364 | 'image-size' => 'size',
365 | 'image_alt' => 'alt',
366 | 'post_excerpt' => 'caption',
367 | 'width' => 'width',
368 | );
369 |
370 | $shortcode_ui_def = self::get_shortcode_ui_args();
371 | $encoded_attributes = wp_list_pluck(
372 | array_filter( $shortcode_ui_def['attrs'], function( $attr ) {
373 | return ! empty( $attr['encode'] ) && $attr['encode'];
374 | } ),
375 | 'attr'
376 | );
377 |
378 | foreach ( $allowed_attrs as $attachment_attr => $shortcode_attr ) {
379 | if ( ! empty( $attachment[ $attachment_attr ] ) ) {
380 | $shortcode_attrs[ $shortcode_attr ] = in_array( $shortcode_attr, $encoded_attributes, true ) ?
381 | rawurlencode( $attachment[ $attachment_attr ] ) : $attachment[ $attachment_attr ];
382 | }
383 | }
384 |
385 | /**
386 | * Filter the shortcode attributes when inserting image from the media library.
387 | *
388 | * @param array Shortcode attributes, as generated by the plugin
389 | * @param string $html Generated by `wp_ajax_send_attachment_to_editor()`
390 | * @param int $id Attachment ID
391 | * @param array $attachment Attributes selected in the media editor
392 | */
393 | $shortcode_attrs = apply_filters( 'img_shortcode_send_to_editor_attrs', $shortcode_attrs, $html, $attachment_id, $attachment );
394 |
395 | $shortcode = '[img ';
396 |
397 | foreach ( $shortcode_attrs as $attr_name => $attr_value ) {
398 | $shortcode .= sanitize_key( $attr_name ) . '="' . esc_attr( $attr_value ) . '" ';
399 | }
400 |
401 | $shortcode .= '/]';
402 |
403 | return $shortcode;
404 | }
405 |
406 | }
407 |
--------------------------------------------------------------------------------
/inc/class-wp-cli-img-shortcode-command.php:
--------------------------------------------------------------------------------
1 | fetcher = new \WP_CLI\Fetchers\Post;
11 | }
12 |
13 |
14 | /**
15 | * Migrate post content from tags to image shortcodes.
16 | *
17 | * ## OPTIONS
18 | *
19 | * ...
20 | * : One or more IDs of posts to update.
21 | *
22 | * [--dry-run]
23 | * : Only show the content which is to be changed, don't update posts.
24 | *
25 | * ## EXAMPLES
26 | *
27 | * ## Migrate all Posts to the Image Shortcake syntax
28 | * wp image-shortcake-shortcode migrate `wp post list --post_type=post` --ids`
29 | *
30 | * ## Converts images to shortcodes on one post, preserving a log to rollback in case of errors.
31 | * wp image-shortcake migrate 123 > potential-oops.txt
32 | *
33 | *
34 | * @synopsis ... [--dry-run]
35 | */
36 | public function migrate( $args, $assoc_args ) {
37 |
38 | foreach ( array_filter( $args ) as $post_id ) {
39 |
40 | $post = $this->fetcher->get_check( $post_id );
41 |
42 | $_content = $post->post_content;
43 |
44 | $caption_replacements = Img_Shortcode_Data_Migration::find_caption_shortcodes_for_replacement( $_content );
45 |
46 | $_content = str_replace(
47 | array_keys( $caption_replacements ),
48 | array_values( $caption_replacements ),
49 | $_content
50 | );
51 |
52 | $img_tag_replacements = Img_Shortcode_Data_Migration::find_img_tags_for_replacement( $_content );
53 |
54 | $_content = str_replace(
55 | array_keys( $img_tag_replacements ),
56 | array_values( $img_tag_replacements ),
57 | $_content
58 | );
59 |
60 | $replacements = array_merge( (array) $caption_replacements, (array) $img_tag_replacements );
61 |
62 | WP_CLI::log( '' );
63 |
64 | if ( 0 === count( $replacements ) ) {
65 | WP_CLI::log( 'Nothing to replace on post ' . $post->ID . '. Skipping.' );
66 | WP_CLI::log( '' );
67 | continue;
68 | }
69 |
70 | $header = 'Image shortcode replacements for post ' . $post->ID;
71 |
72 | WP_CLI::log( $header );
73 | WP_CLI::log( str_repeat( '=', strlen( $header ) ) );
74 | WP_CLI::log( '' );
75 |
76 | foreach ( $replacements as $del => $ins ) {
77 | \WP_CLI::log( \cli\Colors::colorize( '%C-%n' ) . $del, true );
78 | \WP_CLI::log( \cli\Colors::colorize( '%G+%n' ) . $ins, true );
79 | }
80 |
81 | WP_CLI::log( '' );
82 |
83 | if ( isset( $assoc_args['dry-run'] ) ) {
84 | WP_CLI::log( 'Post not updated: --dry-run specifed.' );
85 | WP_CLI::log( '' );
86 | continue;
87 | }
88 |
89 | global $wpdb;
90 |
91 | // @codingStandardsIgnoreStart
92 | $updated = $wpdb->update( $wpdb->posts, array( 'post_content' => $_content ), array( 'ID' => $post_id ) );
93 | // @codingStandardsIgnoreEnd
94 |
95 | if ( 1 === $updated ) {
96 | clean_post_cache( $post );
97 | WP_CLI::success( 'Updated post ' . $post->ID . '.' );
98 | } else {
99 | WP_CLI::warning( 'There was an unexpected error updating post ' . $post->ID . '.' );
100 | }
101 | }
102 |
103 | }
104 |
105 | }
106 |
107 |
108 | WP_CLI::add_command( 'image-shortcake', 'Image_Shortcake_Command' );
109 |
--------------------------------------------------------------------------------
/languages/image-shortcake-ru_RU.mo:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wp-shortcake/image-shortcake/cf344bbe6e32bdf273294f8609bddbe8835a5609/languages/image-shortcake-ru_RU.mo
--------------------------------------------------------------------------------
/languages/image-shortcake-ru_RU.po:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 fusionengineering, goldenapples
2 | # This file is distributed under the same license as the Image-shortcake package.
3 | msgid ""
4 | msgstr ""
5 | "Project-Id-Version: Image-shortcake 0.1.0\n"
6 | "Report-Msgid-Bugs-To: http://wordpress.org/support/plugin/image-shortcake\n"
7 | "POT-Creation-Date: 2015-07-03 11:00+0300\n"
8 | "PO-Revision-Date: 2015-07-03 11:02+0300\n"
9 | "Last-Translator: Sergey Sedykh \n"
10 | "Language-Team: \n"
11 | "Language: ru_RU\n"
12 | "MIME-Version: 1.0\n"
13 | "Content-Type: text/plain; charset=UTF-8\n"
14 | "Content-Transfer-Encoding: 8bit\n"
15 | "X-Generator: Poedit 1.8.1\n"
16 | "X-Poedit-KeywordsList: __;_e;_x:1,2c;_ex:1,2c;_n:1,2;_nx:1,2,4c;_n_noop:1,2;"
17 | "_nx_noop:1,2,3c;esc_attr__;esc_html__;esc_attr_e;esc_html_e;esc_attr_x:1,2c;"
18 | "esc_html_x:1,2c\n"
19 | "Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
20 | "%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
21 | "X-Poedit-SourceCharset: UTF-8\n"
22 | "X-Poedit-Basepath: ../\n"
23 | "X-Textdomain-Support: yes\n"
24 | "X-Poedit-SearchPath-0: .\n"
25 |
26 | #: image-shortcake.php:107
27 | msgid ""
28 | "Shortcode UI plugin is not active. No UI will be available for the image "
29 | "shortcode."
30 | msgstr ""
31 | "Плагин Shortcode UI не активирован. Для шорткода изображения не будет виден UI."
32 |
33 | #: inc/class-img-shortcode.php:22
34 | msgid "Thumbnail"
35 | msgstr "Миниатюра"
36 |
37 | #: inc/class-img-shortcode.php:23
38 | msgid "Medium"
39 | msgstr "Средний"
40 |
41 | #: inc/class-img-shortcode.php:24
42 | msgid "Large"
43 | msgstr "Большой"
44 |
45 | #: inc/class-img-shortcode.php:25
46 | msgid "Full size"
47 | msgstr "Полный"
48 |
49 | #: inc/class-img-shortcode.php:46
50 | msgid "Image"
51 | msgstr "Изображение"
52 |
53 | #: inc/class-img-shortcode.php:53
54 | msgid "Choose Attachment"
55 | msgstr "Выберите изображение"
56 |
57 | #: inc/class-img-shortcode.php:57 inc/class-img-shortcode.php:58
58 | msgid "Select Image"
59 | msgstr "Выберите изображение"
60 |
61 | #: inc/class-img-shortcode.php:62
62 | msgid "Image size"
63 | msgstr "Размер изображения"
64 |
65 | #: inc/class-img-shortcode.php:70
66 | msgid "Alt"
67 | msgstr "Alt"
68 |
69 | #: inc/class-img-shortcode.php:73
70 | msgid "Alt text for the image"
71 | msgstr "Alt для изображения"
72 |
73 | #: inc/class-img-shortcode.php:74 inc/class-img-shortcode.php:82
74 | msgid "Quote marks and HTML tags are not allowed"
75 | msgstr "Кавычки и HTML не допускаются"
76 |
77 | #: inc/class-img-shortcode.php:78
78 | msgid "Caption"
79 | msgstr "Подпись"
80 |
81 | #: inc/class-img-shortcode.php:81
82 | msgid "Caption for the image"
83 | msgstr "Подпись изображения"
84 |
85 | #: inc/class-img-shortcode.php:86
86 | msgid "Alignment"
87 | msgstr "Выравнивание"
88 |
89 | #: inc/class-img-shortcode.php:91
90 | msgid "Left"
91 | msgstr "Слева"
92 |
93 | #: inc/class-img-shortcode.php:92
94 | msgid "Center"
95 | msgstr "По центру"
96 |
97 | #: inc/class-img-shortcode.php:93
98 | msgid "Right"
99 | msgstr "Справа"
100 |
101 | #: inc/class-img-shortcode.php:94
102 | msgid "None"
103 | msgstr "Нет"
104 |
105 | #: inc/class-img-shortcode.php:99
106 | msgid "Link to"
107 | msgstr "Ссылка на"
108 |
109 | #: inc/class-img-shortcode.php:104
110 | msgid "None (no link)"
111 | msgstr "Нет (без ссылки)"
112 |
113 | #: inc/class-img-shortcode.php:105
114 | msgid "Link to attachment file"
115 | msgstr "Ссылка файл изображения"
116 |
117 | #: inc/class-img-shortcode.php:106
118 | msgid "Link to file"
119 | msgstr "Ссылка на файл"
120 |
121 | #: inc/class-img-shortcode.php:107 inc/class-img-shortcode.php:112
122 | msgid "Custom link"
123 | msgstr "Произвольная ссылка"
124 |
125 | #: inc/class-img-shortcode.php:115
126 | msgid "URL to link to (if above link is \"custom\")"
127 | msgstr "URL для ссылки, если сверху выбрано \"Произвольная ссылка\""
128 |
129 | #~ msgid "Image-shortcake"
130 | #~ msgstr "Image-shortcake"
131 |
132 | #~ msgid "https://github.com/fusioneng/image-shortcake"
133 | #~ msgstr "https://github.com/fusioneng/image-shortcake"
134 |
135 | #~ msgid ""
136 | #~ "Provides a shortcode for image elements. Use with the Shortcake plugin for "
137 | #~ "a preview of images"
138 | #~ msgstr ""
139 | #~ "Создает шорткод для изображений. Используйте вместе с плагином Shortcake."
140 |
141 | #~ msgid "fusionengineering, goldenapples"
142 | #~ msgstr "fusionengineering, goldenapples"
143 |
144 | #~ msgid "https://github.com/fusioneng"
145 | #~ msgstr "https://github.com/fusioneng"
146 |
--------------------------------------------------------------------------------
/languages/image-shortcake.pot:
--------------------------------------------------------------------------------
1 | # Copyright (C) 2015 fusionengineering, goldenapples
2 | # This file is distributed under the same license as the Image-shortcake package.
3 | msgid ""
4 | msgstr ""
5 | "Project-Id-Version: Image-shortcake 0.1.0\n"
6 | "Report-Msgid-Bugs-To: http://wordpress.org/support/plugin/image-shortcake\n"
7 | "POT-Creation-Date: 2015-06-03 23:38:07+00:00\n"
8 | "MIME-Version: 1.0\n"
9 | "Content-Type: text/plain; charset=utf-8\n"
10 | "Content-Transfer-Encoding: 8bit\n"
11 | "PO-Revision-Date: 2015-MO-DA HO:MI+ZONE\n"
12 | "Last-Translator: FULL NAME \n"
13 | "Language-Team: LANGUAGE \n"
14 | "X-Generator: grunt-wp-i18n 0.5.2\n"
15 | "X-Poedit-KeywordsList: "
16 | "__;_e;_x:1,2c;_ex:1,2c;_n:1,2;_nx:1,2,4c;_n_noop:1,2;_nx_noop:1,2,3c;esc_"
17 | "attr__;esc_html__;esc_attr_e;esc_html_e;esc_attr_x:1,2c;esc_html_x:1,2c;\n"
18 | "Language: en\n"
19 | "Plural-Forms: nplurals=2; plural=(n != 1);\n"
20 | "X-Poedit-Country: United States\n"
21 | "X-Poedit-SourceCharset: UTF-8\n"
22 | "X-Poedit-Basepath: ../\n"
23 | "X-Poedit-SearchPath-0: .\n"
24 | "X-Poedit-Bookmarks: \n"
25 | "X-Textdomain-Support: yes\n"
26 |
27 | #: image-shortcake.php:107
28 | msgid ""
29 | "Shortcode UI plugin is not active. No UI will be available for the image "
30 | "shortcode."
31 | msgstr ""
32 |
33 | #: inc/class-img-shortcode.php:22
34 | msgid "Thumbnail"
35 | msgstr ""
36 |
37 | #: inc/class-img-shortcode.php:23
38 | msgid "Medium"
39 | msgstr ""
40 |
41 | #: inc/class-img-shortcode.php:24
42 | msgid "Large"
43 | msgstr ""
44 |
45 | #: inc/class-img-shortcode.php:25
46 | msgid "Full size"
47 | msgstr ""
48 |
49 | #: inc/class-img-shortcode.php:46
50 | msgid "Image"
51 | msgstr ""
52 |
53 | #: inc/class-img-shortcode.php:53
54 | msgid "Choose Attachment"
55 | msgstr ""
56 |
57 | #: inc/class-img-shortcode.php:57 inc/class-img-shortcode.php:58
58 | msgid "Select Image"
59 | msgstr ""
60 |
61 | #: inc/class-img-shortcode.php:62
62 | msgid "Image size"
63 | msgstr ""
64 |
65 | #: inc/class-img-shortcode.php:70
66 | msgid "Alt"
67 | msgstr ""
68 |
69 | #: inc/class-img-shortcode.php:73
70 | msgid "Alt text for the image"
71 | msgstr ""
72 |
73 | #: inc/class-img-shortcode.php:74 inc/class-img-shortcode.php:82
74 | msgid "Quote marks and HTML tags are not allowed"
75 | msgstr ""
76 |
77 | #: inc/class-img-shortcode.php:78
78 | msgid "Caption"
79 | msgstr ""
80 |
81 | #: inc/class-img-shortcode.php:81
82 | msgid "Caption for the image"
83 | msgstr ""
84 |
85 | #: inc/class-img-shortcode.php:86
86 | msgid "Alignment"
87 | msgstr ""
88 |
89 | #: inc/class-img-shortcode.php:91
90 | msgid "Left"
91 | msgstr ""
92 |
93 | #: inc/class-img-shortcode.php:92
94 | msgid "Center"
95 | msgstr ""
96 |
97 | #: inc/class-img-shortcode.php:93
98 | msgid "Right"
99 | msgstr ""
100 |
101 | #: inc/class-img-shortcode.php:94
102 | msgid "None"
103 | msgstr ""
104 |
105 | #: inc/class-img-shortcode.php:99
106 | msgid "Link to"
107 | msgstr ""
108 |
109 | #: inc/class-img-shortcode.php:104
110 | msgid "None (no link)"
111 | msgstr ""
112 |
113 | #: inc/class-img-shortcode.php:105
114 | msgid "Link to attachment file"
115 | msgstr ""
116 |
117 | #: inc/class-img-shortcode.php:106
118 | msgid "Link to file"
119 | msgstr ""
120 |
121 | #: inc/class-img-shortcode.php:107 inc/class-img-shortcode.php:112
122 | msgid "Custom link"
123 | msgstr ""
124 |
125 | #: inc/class-img-shortcode.php:115
126 | msgid "URL to link to (if above link is \"custom\")"
127 | msgstr ""
128 |
129 | #. Plugin Name of the plugin/theme
130 | msgid "Image-shortcake"
131 | msgstr ""
132 |
133 | #. Plugin URI of the plugin/theme
134 | msgid "https://github.com/fusioneng/image-shortcake"
135 | msgstr ""
136 |
137 | #. Description of the plugin/theme
138 | msgid ""
139 | "Provides a shortcode for image elements. Use with the Shortcake plugin for "
140 | "a preview of images"
141 | msgstr ""
142 |
143 | #. Author of the plugin/theme
144 | msgid "fusionengineering, goldenapples"
145 | msgstr ""
146 |
147 | #. Author URI of the plugin/theme
148 | msgid "https://github.com/fusioneng"
149 | msgstr ""
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Image-shortcake",
3 | "version": "0.0.0",
4 | "repository": {
5 | "type": "git",
6 | "url": "https://github.com/fusioneng/image-shortcake.git"
7 | },
8 | "main": "Gruntfile.js",
9 | "author": "Fusion Engineering ",
10 | "devDependencies": {
11 | "grunt": "^0.4.5",
12 | "grunt-contrib-watch": "^0.6.1",
13 | "grunt-phpcs": "^0.4.0",
14 | "grunt-wp-i18n": "^0.5.0",
15 | "grunt-wp-readme-to-markdown": "~0.9.0"
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/phpcs.ruleset.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Sniffs for the coding standards of the Image Shortcake
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/phpunit.xml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
11 | ./tests/
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/readme.txt:
--------------------------------------------------------------------------------
1 | === Image Shortcake ===
2 | Contributors: fusionengineering, goldenapples, danielbachhuber
3 | Tags: shortcodes, images
4 | Requires at least: 3.0.1
5 | Tested up to: 4.3
6 | Stable tag: 0.1.0
7 | License: GPLv2 or later
8 | License URI: http://www.gnu.org/licenses/gpl-2.0.html
9 |
10 | Image Shortcake adds a shortcode for images, so that themes can template and filter images displayed in posts. Although it can be used standalone, it is designed to work with the UI provided by the [Shortcake (Shortcode UI)](https://github.com/fusioneng/Shortcake) plugin.
11 |
12 | == Description ==
13 |
14 | When images are inserted into posts from the media library or media uploader, only the html of the ` ` tag and the link around it (if any) are preserved. This means that themes which want to change the way images are marked up in content don't have an easy way of doing this.
15 |
16 | Image Shortcake is an attempt to solve this problem, by saving images in post content as _shortcodes_ rather than HTML. The output of shortcodes can be easily filtered in themes, plugins and templates, and since the original attachment data is preseved as attributes on the shortcode, it becomes much easier for modify the way images are marked up in themes.
17 |
18 | For best results, use this with the [Shortcake (Shortcode UI)](https://github.com/fusioneng/Shortcake) plugin. Shortcake offers an easy to use interface to manage shortcodes in post content.
19 |
20 | What could you use this for? Well, at [Fusion](http://fusion.net) we use this shortcode for:
21 |
22 | * **Responsive Images**. By filtering the output of the `[img]` shortcode image tag, we're able to insert the `srcset` attribute, so that all of the images on our site are served responsively to browsers that support that.
23 |
24 | * **Inline sharing buttons**. We've added share links to each of the images on our site. Because these are inserted through a filter on a shortcode and not in the post content, it's easy to modify them on the fly. And having this logic in template files rather in on-page javascript that runs after page load makes it quicker for users.
25 |
26 | * **Photo credits**. We've added "credit" as an image meta field, and we use a filter on 'img_shortcode_output_after_linkify' to display it on all images.
27 |
28 | See the [Installation](#Installation) section for more ideas and tips for custom image templates. [Get involved with the project](https://github.com/fusioneng/image-shortcake) on Github.
29 |
30 | == Installation ==
31 |
32 | = Customizing Output =
33 |
34 | The whole point of using shortcodes rather than HTML tags for images is that you can customize the markup of images in your theme. This plugin offers three primary hooks to modify the output:
35 |
36 | * `img_shortcode_output_img_tag`: Filters the output of the tag before wrapping it in link or caption
37 | * `img_shortcode_output_after_linkify`: Filters the output of the tag after wrapping in link
38 | * `img_shortcode_output_after_captionify`: Filters the output of the tag after wrapping in link and attaching caption
39 |
40 | You can, of course, disregard the markup generated by this plugin altogether and use a template part for images if you want. This example adds EXIF data below all images containing those fields, in all post content:
41 |
42 | in the theme's `functions.php`:
43 |
44 | ```
45 | add_filter( 'img_shortcode_output_img_tag', 'load_image_template', 10, 2 );
46 |
47 | function load_image_template( $_, $attributes ) {
48 | ob_start();
49 | get_template_part( 'inline-image' );
50 | return ob_get_clean();
51 | }
52 | ```
53 |
54 | in a template file called `inline-image.php`:
55 |
56 | ```
57 | "$class $align attachment-$size",
72 | 'alt' => $alt,
73 | )
74 | );
75 |
76 | if ( is_array( $exif_data ) ) {
77 | echo '';
82 | }
83 | ```
84 |
85 |
86 | = Data Migration =
87 |
88 | The plugin comes with two [WP-CLI](http://wp-cli.org) commands to migrate images in your existing content into the `[img]` shortcode format used by
89 | this plugin. _Note: if it isn't clear, this is an early release -- use at your own risk, and make sure you've backed up your posts before migrating!_
90 |
91 | `wp image-shortcake migrate [--dry-run]`
92 |
93 | This command searches the post content of the posts specified in ``, and replaces any ` ` tags or `[caption]` shortcodes with `[img]`
94 | shortcodes. Currently it only catches images added through the media library; custom img tags will not be converted.
95 |
96 | If you add the `--dry-run` flag, no replacements will actually be performed, just a summary report of the changes which would have been made.
97 |
98 | `wp image-shortcake revert [--dry-run]`
99 |
100 | This command finds all `[img]` shortcodes in the content of any of the posts specified in ``, and replaces them with the markup that would be generated by those shortcodes. Note that this runs any filters in your theme, so that if you have filtered the output of the shortcodes at any output, those filters will be reflected in the coverted post content.
101 |
102 | == Screenshots ==
103 |
104 | 1. This is the shortcode UI form as accessed from **Insert Media > Insert Post Element**. (Note that you can also insert images as usual, by inserting them in the Media Library - they will be transparently converted into shortcodes behind the scenes for you.)
105 | 2. Once inserted into a post, the image preview renders in the editor just as it normally would. The Shortcake plugin's edit/delete buttons are available to modify the shortcode through the provided UI.
106 |
107 | == Changelog ==
108 |
109 | = 0.1.0 (May 1, 2015) =
110 | * Initial release
111 |
--------------------------------------------------------------------------------
/screenshot-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wp-shortcake/image-shortcake/cf344bbe6e32bdf273294f8609bddbe8835a5609/screenshot-1.png
--------------------------------------------------------------------------------
/screenshot-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wp-shortcake/image-shortcake/cf344bbe6e32bdf273294f8609bddbe8835a5609/screenshot-2.png
--------------------------------------------------------------------------------
/tests/bootstrap.php:
--------------------------------------------------------------------------------
1 | insert_attachment( null,
17 | dirname( __FILE__ ) . '/data/fusion_image_placeholder_16x9_h2000.png',
18 | array(
19 | 'post_title' => 'Post',
20 | 'post_content' => 'Post Content',
21 | 'post_date' => '2014-10-01 17:28:00',
22 | 'post_status' => 'publish',
23 | 'post_type' => 'attachment',
24 | )
25 | );
26 |
27 | $this->attachment_id = $attachment_id;
28 |
29 | $upload_dir = wp_upload_dir();
30 |
31 | $this->image_src = $upload_dir['url'] . '/fusion_image_placeholder_16x9_h2000.png';
32 | $this->image_path = $upload_dir['path'] . '/fusion_image_placeholder_16x9_h2000.png';
33 |
34 | $this->image_tag_from_attachment =
35 | ' image_src . '" ' .
37 | 'alt="This is the alt attribute." ' .
38 | 'width="1024" height="540" />';
39 |
40 | $this->image_tag_from_src =
41 | '' .
42 | ' image_src . '" ' .
44 | 'alt="This is the alt attribute." ' .
45 | 'width="1024" height="540" />' .
46 | ' ';
47 |
48 | }
49 |
50 | public function tearDown() {
51 | parent::tearDown();
52 |
53 | unlink( $this->image_path );
54 | }
55 | // @codingStandardsIgnoreEnd
56 |
57 |
58 | /**
59 | * Case: tags where the src is an attachment
60 | *
61 | */
62 | function test_img_tag_from_attachment() {
63 | $img_tag = $this->image_tag_from_attachment;
64 | $post_content = "blah blah blah\r\n\r\n{$this->image_tag_from_attachment}";
65 | $post_id = wp_insert_post( array( 'post_content' => $post_content ) );
66 |
67 | $replacements = Img_Shortcode_Data_Migration::find_img_tags_for_replacement_on_post( $post_id );
68 |
69 | $this->assertContains( $img_tag, array_keys( $replacements ) );
70 |
71 | $this->assertContains( 'attachment="' . $this->attachment_id . '"', $replacements[ $img_tag ] );
72 | $this->assertNotContains( 'src="', $replacements[ $img_tag ] );
73 | }
74 |
75 | /**
76 | * Case: tags with an external src
77 | *
78 | */
79 | function test_img_tag_from_src() {
80 | $img_tag =
81 | '' .
82 | ' image_src . '" ' .
84 | 'alt="This is the alt attribute." ' .
85 | 'width="1024" height="540" />' .
86 | ' ';
87 |
88 | $post_id = wp_insert_post( array( 'post_content' => "\r\n\r\n$img_tag\r\nblah blah blah" ) );
89 |
90 | $replacements = Img_Shortcode_Data_Migration::find_img_tags_for_replacement_on_post( $post_id );
91 |
92 | $this->assertNotContains( 'attachment="', $replacements[ $img_tag ] );
93 | $this->assertContains( 'src="' . $this->image_src . '"', $replacements[ $img_tag ] );
94 |
95 | }
96 |
97 | /**
98 | * Case: tags wrapped in links
99 | *
100 | */
101 | public function test_img_tags_wrapped_in_links() {
102 | $img_tag = $this->image_tag_from_attachment;
103 |
104 | $img_tag_link_custom =
105 | '' . $img_tag . ' ';
106 |
107 | $img_tag_link_file =
108 | '' . $img_tag . ' ';
109 |
110 | $img_tag_link_attachment =
111 | '' . $img_tag . ' ';
112 |
113 | $post_content = "$img_tag\r\n$img_tag_link_custom\r\n$img_tag_link_file\r\n$img_tag_link_attachment";
114 |
115 | $post_id = wp_insert_post( array( 'post_content' => $post_content ) );
116 |
117 | $replacements = Img_Shortcode_Data_Migration::find_img_tags_for_replacement_on_post( $post_id );
118 |
119 | foreach ( array( $img_tag, $img_tag_link_custom, $img_tag_link_file, $img_tag_link_attachment ) as $should_be_matched ) {
120 | $this->assertContains( $should_be_matched, array_keys( $replacements ) );
121 | }
122 |
123 | $this->assertContains( 'attachment="' . $this->attachment_id . '"', $replacements[ $img_tag ] );
124 | $this->assertNotContains( 'src="', $replacements[ $img_tag ] );
125 |
126 | $this->assertContains( 'href="http://go.to/thislink/"', $replacements[ $img_tag_link_custom ] );
127 | $this->assertNotContains( 'linkto=', $replacements[ $img_tag_link_custom ] );
128 |
129 | $this->assertNotContains( 'href=', $replacements[ $img_tag_link_file ] );
130 | $this->assertContains( 'linkto="file"', $replacements[ $img_tag_link_file ] );
131 |
132 | $this->assertNotContains( 'href=', $replacements[ $img_tag_link_attachment ] );
133 | $this->assertContains( 'linkto="attachment"', $replacements[ $img_tag_link_attachment ] );
134 | }
135 |
136 |
137 | /**
138 | * Case: [caption] shortcodes containing any of the above items
139 | *
140 | */
141 | public function test_replace_caption_shortcodes() {
142 | $caption_no_link = '[caption]' . $this->image_tag_from_src . ' Caption of image without attachment[/caption]';
143 |
144 | $caption_with_link = '[caption width="1024"]' .
145 | '' . $this->image_tag_from_attachment . ' ' .
146 | ' Caption of image linked to attachment page' .
147 | '[/caption]';
148 |
149 | $post_content = "Post content.\r\n\r\n$caption_no_link\r\n\r\n$caption_with_link";
150 |
151 | $post_id = wp_insert_post( array( 'post_content' => $post_content ) );
152 |
153 | $replacements = Img_Shortcode_Data_Migration::find_caption_shortcodes_for_replacement_on_post( $post_id );
154 |
155 | $this->assertCount( 2, $replacements );
156 | $this->assertContains( 'caption="Caption of image without attachment"', $replacements[ $caption_no_link ] );
157 | $this->assertContains( 'caption="Caption of image linked to attachment page"', $replacements[ $caption_with_link ] );
158 | }
159 |
160 |
161 | /**
162 | * Helper function: insert an attachment to test properties of.
163 | *
164 | * @param int $parent_post_id
165 | * @param str path to image to use
166 | * @param array $post_fields Fields, in the format to be sent to `wp_insert_post()`
167 | * @return int Post ID of inserted attachment
168 | */
169 | private function insert_attachment( $parent_post_id = 0, $image = null, $post_fields = array() ) {
170 |
171 | $filename = rand_str() . '.jpg';
172 | $contents = rand_str();
173 |
174 | if ( $image ) {
175 | // @codingStandardsIgnoreStart
176 | $filename = basename( $image );
177 | $contents = file_get_contents( $image );
178 | // @codingStandardsIgnoreEnd
179 | }
180 |
181 | $upload = wp_upload_bits( $filename, null, $contents );
182 | $this->assertTrue( empty( $upload['error'] ) );
183 |
184 | $type = '';
185 | if ( ! empty( $upload['type'] ) ) {
186 | $type = $upload['type'];
187 | } else {
188 | $mime = wp_check_filetype( $upload['file'] );
189 | if ( $mime ) {
190 | $type = $mime['type'];
191 | }
192 | }
193 |
194 | $attachment = wp_parse_args( $post_fields,
195 | array(
196 | 'post_title' => basename( $upload['file'] ),
197 | 'post_content' => 'Test Attachment',
198 | 'post_type' => 'attachment',
199 | 'post_parent' => $parent_post_id,
200 | 'post_mime_type' => $type,
201 | 'guid' => $upload['url'],
202 | )
203 | );
204 |
205 | // Save the data
206 | $id = wp_insert_attachment( $attachment, $upload['file'], $parent_post_id );
207 | wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $upload['file'] ) );
208 |
209 | return $id;
210 | }
211 |
212 | }
213 |
214 |
--------------------------------------------------------------------------------
/tests/test-img-shortcode.php:
--------------------------------------------------------------------------------
1 | attachment_id = $this->insert_attachment( null,
14 | dirname( __FILE__ ) . '/data/fusion_image_placeholder_16x9_h2000.png',
15 | array(
16 | 'post_title' => 'Post',
17 | 'post_content' => 'Post Content',
18 | 'post_date' => '2014-10-01 17:28:00',
19 | 'post_status' => 'publish',
20 | 'post_type' => 'attachment',
21 | )
22 | );
23 |
24 | $upload_dir = wp_upload_dir();
25 |
26 | $this->image_src = $upload_dir['url'] . '/fusion_image_placeholder_16x9_h2000.png';
27 | $this->image_path = $upload_dir['path'] . '/fusion_image_placeholder_16x9_h2000.png';
28 | }
29 |
30 | public function tearDown() {
31 | parent::tearDown();
32 |
33 | unlink( $this->image_path );
34 | }
35 | // @codingStandardsIgnoreEnd
36 |
37 | function test_construct_ui() {
38 | // replace this with some actual testing code
39 | $this->assertTrue( true );
40 | }
41 |
42 |
43 | /*
44 | * Simplest case: An [img] shortcode with a url passed as a src argument
45 | * should just render an image with that src.
46 | */
47 | function test_img_shortcode_with_src_tag() {
48 | $str = '[img src="http://example.com/example.jpg" align="alignleft" /]';
49 | $content = apply_filters( 'the_content', $str );
50 |
51 | $this->assertContains( ' ', $content );
52 | }
53 |
54 |
55 | /**
56 | * Create an actual attachment, and test that the link and caption elements
57 | * are implemented correctly.
58 | */
59 | function test_img_shortcode_from_attachment() {
60 | $attachment_id = $this->attachment_id;
61 |
62 | $upload_dir = wp_upload_dir();
63 |
64 | // Test image src
65 | $content = apply_filters( 'the_content', '[img attachment="' . $attachment_id . '" /]' );
66 |
67 | $expected_src_attr = $upload_dir['url'] . '/fusion_image_placeholder_16x9_h2000.png';
68 | $this->assertContains( 'src="' . $expected_src_attr . '"', $content );
69 |
70 | // Test link href: linkto="file"
71 | $content = apply_filters( 'the_content', '[img attachment="' . $attachment_id . '" linkto="file" /]' );
72 |
73 | $expected_href_attr = $upload_dir['url'] . '/fusion_image_placeholder_16x9_h2000.png';
74 | $this->assertContains( 'href="' . $expected_href_attr . '"', $content );
75 |
76 | // Test link href: linkto="attachment"
77 | $content = apply_filters( 'the_content', '[img attachment="' . $attachment_id . '" linkto="attachment" /]' );
78 |
79 | $expected_href_attr = get_permalink( $attachment_id );
80 | $this->assertContains( 'href="' . $expected_href_attr . '"', $content );
81 |
82 | // Test caption attribute
83 | $caption = <<caption". It should contain HTML and markup .
85 | EOL;
86 | $content = apply_filters( 'the_content', '[img attachment="' . $attachment_id . '" caption="' . esc_attr( $caption ) . '" /]' );
87 |
88 | $expected_caption = esc_html( $caption );
89 | $this->assertContains( $expected_caption , $content );
90 | }
91 |
92 |
93 | /**
94 | * Test that an image inserted from the editor is transformed into a
95 | * shortcode correctly.
96 | */
97 | function test_image_send_to_editor() {
98 |
99 | $attachment_data = array(
100 | 'post_title' => 'Post',
101 | 'post_content' => 'Post Content',
102 | 'post_date' => '2014-10-01 17:28:00',
103 | 'post_status' => 'publish',
104 | 'post_type' => 'attachment',
105 | );
106 |
107 | $attachment_id = $this->insert_attachment( null,
108 | dirname( __FILE__ ) . '/data/fusion_image_placeholder_16x9_h2000.png',
109 | $attachment_data
110 | );
111 |
112 | /**
113 | * Fields in the media editor form
114 | */
115 | $attachment_data = array_merge(
116 | $attachment_data,
117 | array(
118 | 'id' => $attachment_id,
119 | 'post_content' => 'This is the "description"',
120 | 'post_excerpt' => 'This is the [caption]',
121 | 'align' => 'right',
122 | 'image-size' => 'large',
123 | 'image_alt' => 'This is the \'alt\'',
124 | 'url' => get_permalink( $attachment_id ),
125 | )
126 | );
127 |
128 | $shortcode = apply_filters( 'media_send_to_editor', '', $attachment_id, $attachment_data );
129 |
130 | $this->assertContains( '[img ', $shortcode );
131 | $this->assertContains( 'size="large"', $shortcode );
132 | $this->assertContains( 'align="alignright"', $shortcode );
133 | $this->assertContains( 'alt="This%20is%20the%20%27alt%27"', $shortcode );
134 | $this->assertContains( 'caption="This%20is%20the%20%5Bcaption%5D"', $shortcode );
135 |
136 | }
137 |
138 |
139 | /**
140 | * Test that an audio or video file inserted from the editor is NOT
141 | * transformed into the img shortcode.
142 | */
143 | function test_other_media_send_to_editor() {
144 |
145 | $attachment_data = array(
146 | 'post_title' => 'Audio',
147 | 'post_content' => 'Audio Content',
148 | 'post_date' => '2014-10-01 17:28:00',
149 | 'post_status' => 'publish',
150 | 'post_type' => 'attachment',
151 | );
152 |
153 | $attachment_id = $this->insert_attachment( null,
154 | dirname( __FILE__ ) . '/data/gin_joints.wav',
155 | $attachment_data
156 | );
157 |
158 | $sent_to_editor = apply_filters( 'media_send_to_editor', 'This should be the original audio element.', $attachment_id, $attachment_data );
159 |
160 | $this->assertNotContains( '[img ', $sent_to_editor );
161 |
162 | }
163 |
164 |
165 | /**
166 | * Helper function: insert an attachment to test properties of.
167 | *
168 | * @param int $parent_post_id
169 | * @param str path to image to use
170 | * @param array $post_fields Fields, in the format to be sent to `wp_insert_post()`
171 | * @return int Post ID of inserted attachment
172 | */
173 | private function insert_attachment( $parent_post_id = 0, $image = null, $post_fields = array() ) {
174 |
175 | $filename = rand_str() . '.jpg';
176 | $contents = rand_str();
177 |
178 | if ( $image ) {
179 | // @codingStandardsIgnoreStart
180 | $filename = basename( $image );
181 | $contents = file_get_contents( $image );
182 | // @codingStandardsIgnoreEnd
183 | }
184 |
185 | $upload = wp_upload_bits( $filename, null, $contents );
186 | $this->assertTrue( empty( $upload['error'] ) );
187 |
188 | $type = '';
189 | if ( ! empty( $upload['type'] ) ) {
190 | $type = $upload['type'];
191 | } else {
192 | $mime = wp_check_filetype( $upload['file'] );
193 | if ( $mime ) {
194 | $type = $mime['type'];
195 | }
196 | }
197 |
198 | $attachment = wp_parse_args( $post_fields,
199 | array(
200 | 'post_title' => basename( $upload['file'] ),
201 | 'post_content' => 'Test Attachment',
202 | 'post_type' => 'attachment',
203 | 'post_parent' => $parent_post_id,
204 | 'post_mime_type' => $type,
205 | 'guid' => $upload['url'],
206 | )
207 | );
208 |
209 | // Save the data
210 | $id = wp_insert_attachment( $attachment, $upload['file'], $parent_post_id );
211 | wp_update_attachment_metadata( $id, wp_generate_attachment_metadata( $id, $upload['file'] ) );
212 |
213 | return $id;
214 | }
215 |
216 | }
217 |
218 |
--------------------------------------------------------------------------------