├── lang ├── acf-image_crop-de_DE.mo ├── README.md └── acf-image_crop-de_DE.po ├── css ├── options.css └── input.css ├── acf-image-crop.php ├── js ├── options-v4.js ├── options.js ├── input-v4.js ├── input-old.js └── input.js ├── README.md ├── image_crop-v3.php ├── acf-image-crop-v4.php └── acf-image-crop-v5.php /lang/acf-image_crop-de_DE.mo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andersthorborg/ACF-Image-Crop/HEAD/lang/acf-image_crop-de_DE.mo -------------------------------------------------------------------------------- /lang/README.md: -------------------------------------------------------------------------------- 1 | # Translations directory 2 | 3 | Use this directory to store .po and .mo files. 4 | 5 | This directory can be removed if not used. 6 | -------------------------------------------------------------------------------- /css/options.css: -------------------------------------------------------------------------------- 1 | .field_type-image_crop .dimensions-wrap .acf-input-wrap{ 2 | float:left; 3 | } 4 | .field_type-image_crop .dimensions-wrap .dimension{ 5 | width:150px; 6 | } 7 | .field_type-image_crop label.disabled{ 8 | color: #ccc; 9 | } -------------------------------------------------------------------------------- /acf-image-crop.php: -------------------------------------------------------------------------------- 1 | '.__('Settings','acf-image_crop').''; 48 | // changed END 49 | return $links; 50 | } 51 | 52 | ?> 53 | -------------------------------------------------------------------------------- /js/options-v4.js: -------------------------------------------------------------------------------- 1 | jQuery(function($){ 2 | $(document).on('change', '.field_type-image_crop .target-size-select', function(e) { 3 | if($(this).val() == 'custom'){ 4 | $(this).parents('.field_type-image_crop').find('.dimensions-wrap').removeClass('hidden'); 5 | } 6 | else{ 7 | $(this).parents('.field_type-image_crop').find('.dimensions-wrap').addClass('hidden'); 8 | } 9 | 10 | }); 11 | $(document).on('change', '.field_type-image_crop .crop-type-select', function(e) { 12 | $(this).parents('.field_type-image_crop').find('.dimensions-wrap .dimensions-description').addClass('hidden'); 13 | $(this).parents('.field_type-image_crop').find('.dimensions-wrap .dimensions-description[data-type=' + $(this).val() + ']').removeClass('hidden'); 14 | 15 | }); 16 | $(document).on('click', '.field_type-image_crop .save-in-media-library-select input', function(e) { 17 | var saveToMedia = $(this).val() == 'yes'; 18 | var $returnValueField = $(this).parents('.field_type-image_crop').find('.return-value-select'); 19 | if(! saveToMedia){ 20 | $returnValueField.find('input[value=id]').attr('disabled', true).parents('label').addClass('disabled'); 21 | if($returnValueField.find('input[value=id]').is(':checked')){ 22 | $returnValueField.find('input[value=url]').attr('checked', true); 23 | } 24 | } 25 | else{ 26 | $returnValueField.find('input').removeAttr('disabled').parents('label').removeClass('disabled'); 27 | } 28 | 29 | // $(this).parents('.field_type-image_crop').find('.dimensions-wrap .dimensions-description').addClass('hidden'); 30 | // $(this).parents('.field_type-image_crop').find('.dimensions-wrap .dimensions-description[data-type=' + $(this).val() + ']').removeClass('hidden'); 31 | 32 | }); 33 | $('.field_type-image_crop .save-in-media-library-select input:checked').each(function(){ 34 | $(this).click(); 35 | }); 36 | }); -------------------------------------------------------------------------------- /css/input.css: -------------------------------------------------------------------------------- 1 | .acf-image-crop .crop-stage .crop-image{ 2 | max-width:100%; 3 | } 4 | .acf-image-crop .crop-stage .crop-action{ 5 | float:left; 6 | width:65%; 7 | } 8 | .acf-image-crop .crop-stage .crop-preview{ 9 | float:right; 10 | /* changed */ 11 | width: 30%; 12 | /* changed END */ 13 | } 14 | /* added */ 15 | .acf-image-crop .crop-stage .crop-action h4, 16 | .acf-image-crop .crop-stage .crop-preview h4{ 17 | margin-top:0; 18 | } 19 | .acf-image-crop .error{ 20 | margin-top: 20px !important; 21 | } 22 | /* added END */ 23 | .acf-image-crop .crop-stage .crop-preview .preview{ 24 | width:100%; 25 | } 26 | 27 | .acf-image-crop .crop-stage .crop-preview .crop-controls{ 28 | padding-top:20px; 29 | clear: both; 30 | /* added */ 31 | text-align: right; 32 | /* added END */ 33 | } 34 | 35 | .acf-image-crop .crop-section .crop-stage, .acf-image-crop.cropping .image-section, .acf-image-crop.cropping .init-crop-button{ 36 | display:none; 37 | } 38 | .acf-image-crop.cropping .crop-stage { 39 | display:block; 40 | position:fixed; 41 | top:60px; 42 | /* changed */ 43 | width:880px; 44 | /* changed END */ 45 | left:50%; 46 | margin-left:-440px; 47 | height:auto; 48 | background-color: #fff; 49 | box-shadow: 0px 0px 3px rgba(0,0,0,0.5); 50 | padding: 25px; 51 | /* changed */ 52 | z-index: 11000; 53 | /* changed END */ 54 | 55 | } 56 | .acf-image-crop.cropping .has-image, .acf-image-crop.cropping .crop-section{ 57 | width:100%; 58 | } 59 | 60 | .acf-image-crop .crop-stage.loading .crop-controls{ 61 | -webkit-transition: opacity 300ms; 62 | -moz-transition: opacity 300ms; 63 | -ms-transition: opacity 300ms; 64 | -o-transition: opacity 300ms; 65 | transition: opacity 300ms; 66 | -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=20)"; 67 | filter: alpha(opacity=20); 68 | opacity:0.2; 69 | } 70 | 71 | .button.init-crop-button{ 72 | margin-top: 20px; 73 | } 74 | /* added */ 75 | .button.button-large.button-primary.perform-crop-button { 76 | margin-left:15px; 77 | } 78 | /* added END */ 79 | #acf-image-crop-overlay{ 80 | position:fixed; 81 | /* added */ 82 | z-index: 10000; 83 | /* added END */ 84 | top:0px; 85 | left:0px; 86 | right:0px; 87 | bottom:0px; 88 | background-color: rgba(0,0,0,0.8); 89 | } -------------------------------------------------------------------------------- /js/options.js: -------------------------------------------------------------------------------- 1 | jQuery(function($){ 2 | acf.add_action('append', function(){ 3 | $('.acf-field-object-image-crop .target-size-select').each(function() { 4 | toggleCustomDimensions(this); 5 | }); 6 | $('.acf-field-object-image-crop .save-in-media-library-select input').each(function() { 7 | //console.log(this); 8 | toggleSaveFormats(this); 9 | }); 10 | }); 11 | 12 | $(document).on('change', '.acf-field-object-image-crop .target-size-select', function(e) { 13 | toggleCustomDimensions(this); 14 | }); 15 | 16 | // $(document).on('change', '.acf-field-object-image-crop .crop-type-select', function(e) { 17 | // $(this).parents('.acf-field-object-image-crop').find('.dimensions-wrap .dimensions-description').addClass('hidden'); 18 | // $(this).parents('.acf-field-object-image-crop').find('.dimensions-wrap .dimensions-description[data-type=' + $(this).val() + ']').removeClass('hidden'); 19 | // }); 20 | 21 | $(document).on('click', '.acf-field-object-image-crop .save-in-media-library-select input', function(e) { 22 | toggleSaveFormats(this); 23 | // var saveToMedia = $(this).val() == 'yes'; 24 | // var $returnValueField = $(this).parents('.acf-field-object-image-crop').find('.return-value-select'); 25 | // if(! saveToMedia){ 26 | // $returnValueField.find('input[value=id], input[value=object]').attr('disabled', true).parents('label').addClass('disabled'); 27 | // $returnValueField.find('input[value=url]').attr('checked', true); 28 | // } 29 | // else{ 30 | // $returnValueField.find('input').removeAttr('disabled').parents('label').removeClass('disabled'); 31 | // } 32 | 33 | // $(this).parents('.acf-field-object-image-crop').find('.dimensions-wrap .dimensions-description').addClass('hidden'); 34 | // $(this).parents('.acf-field-object-image-crop').find('.dimensions-wrap .dimensions-description[data-type=' + $(this).val() + ']').removeClass('hidden'); 35 | 36 | }); 37 | 38 | function toggleSaveFormats(saveToMediaSelect){ 39 | if($(saveToMediaSelect).is(':checked')){ 40 | var saveToMedia = $(saveToMediaSelect).val() == 'yes'; 41 | var $returnValueField = $(saveToMediaSelect).parents('.acf-field-object-image-crop').find('.return-value-select'); 42 | if(! saveToMedia){ 43 | $returnValueField.find('input[value=id]').attr('disabled', true).parents('label').addClass('disabled'); 44 | if($returnValueField.find('input[value=id]').is(':checked')){ 45 | $returnValueField.find('input[value=url]').attr('checked', true); 46 | } 47 | } 48 | else{ 49 | $returnValueField.find('input').removeAttr('disabled').parents('label').removeClass('disabled'); 50 | } 51 | } 52 | } 53 | 54 | function toggleCustomDimensions(targetSizeSelect){ 55 | if($(targetSizeSelect).val() == 'custom'){ 56 | $(targetSizeSelect).parents('.acf-field-object-image-crop').first().find('.custom-target-dimension').each(function(){ 57 | $(this).parents('tr.acf-field').first().removeClass('hidden'); 58 | }); 59 | } 60 | else{ 61 | $(targetSizeSelect).parents('.acf-field-object-image-crop').first().find('.custom-target-dimension').each(function(){ 62 | $(this).parents('tr.acf-field').first().addClass('hidden'); 63 | }); 64 | } 65 | } 66 | 67 | $('.acf-field-object-image-crop .target-size-select').each(function() { 68 | toggleCustomDimensions(this); 69 | }); 70 | $('.acf-field-object-image-crop .save-in-media-library-select input').each(function() { 71 | toggleSaveFormats(this); 72 | }); 73 | 74 | }); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Advanced Custom Fields: Image Crop Add-on # 2 | Contributors: andersthorborg 3 | Tags: afc, advanced custom fields, image crop, image, crop 4 | Requires at least: 3.5 5 | Tested up to: 4.6 6 | Stable tag: 1.4.12 7 | License: GPLv2 or later 8 | License URI: http://www.gnu.org/licenses/gpl-2.0.html 9 | 10 | An image field making it possible/required for the user to crop the selected image to the specified image size or dimensions 11 | 12 | ## Description ## 13 | 14 | ACF image crop is an extended version of the native Image-field in ACF. 15 | The field gives the developer/administrator the option to predefine a size for the image, which the user is prompted to crop on the various edit screens. This solves the common issue of images being cropped inappropriately by the automated center-crop, that wordpress performs. 16 | 17 | The plugin supports the defined image sizes as well as a custom option, enabling the developer to specify the dimensions from within the field edit screen. 18 | 19 | The field can be configured to enforce a hard crop or a minimal-dimension-based crop. The hard crop will lock the aspect ratio of the crop where as the minimal-dimension-based crop will not allow the user to crop the image below the specified dimensions. 20 | 21 | This plugin diverts from plugins like [Manual Image Crop](http://wordpress.org/plugins/manual-image-crop/) in that when the user crops an image, a new attachment is generated, so that the relevant crop only applies in the context it is edited. It also keeps the user from dealing with the concept of various image sizes. 22 | 23 | As of version 1.0 the field can be configured to either create the cropped image as a media-item (the default behavior) or simply create it and refer directly to the file without adding it to the media library. This will prevent the media library from being cluttered with several cropped versions of the same image. When this option is selected the only available return type for the field is URL. 24 | 25 | ### Compatibility ### 26 | 27 | This add-on will work with: 28 | 29 | * version 4 and up 30 | 31 | ## Installation ## 32 | 33 | This add-on can be treated as both a WP plugin and a theme include. 34 | 35 | ### Plugin ### 36 | 1. Copy the 'acf-image_crop' folder into your plugins folder 37 | 2. Activate the plugin via the Plugins admin page 38 | 39 | ### Include ### 40 | 1. Copy the 'acf-image_crop' folder into your theme folder (can use sub folders). You can place the folder anywhere inside the 'wp-content' directory 41 | 2. Edit your functions.php file and add the code below (Make sure the path is correct to include the acf-image_crop.php file) 42 | 43 | ` 44 | add_action('acf/register_fields', 'my_register_fields'); 45 | 46 | function my_register_fields() 47 | { 48 | include_once('acf-image-crop/acf-image-crop.php'); 49 | } 50 | ` 51 | 52 | ## Screenshots ## 53 | 54 | 1. Use a registered image size as the field target size 55 | 2. Or use custom dimensions on the fly 56 | 3. On the edit screen, select/upload an image as usual 57 | 4. A crop-button will appear beneath the image (If desired, use the "Force user crop"-option to initialize the crop as soon as the user selects the image) 58 | 5. The image is cropped to the desired format, using the restrictions set under field options 59 | 6. The new format is shown using the specified preview size. The original image is kept with the field, so the image can be re-cropped at any time. 60 | 61 | 62 | ## Changelog ## 63 | 64 | ### 1.4.12 ### 65 | * Fix compatibility with ACF Pro 5.6.0 66 | 67 | ### 1.4.11 ### 68 | * Address issue with changed ACF Pro validation behavior causing php warnings when saving fields 69 | 70 | ### 1.4.10 ### 71 | * Add compatibility with ACF Pro 5.5.5 72 | 73 | ### 1.4.9 ### 74 | * Use acf-image-crop/filename_postfix to allow custom filename postfixes 75 | 76 | ### 1.4.8 ### 77 | * Fix button styling 78 | * Prevent php warnings for unset field settings 79 | 80 | ### 1.4.7 ### 81 | * Added compatibility with ACF PRO 5.4.2.2 icons 82 | 83 | ### 1.4.6 ### 84 | * Added compatibility with ACF 4.4.2 and ACF PRO 5.2.9 85 | 86 | ### 1.4.5 ### 87 | * Added compatibility with ACF 5.2.7 88 | * Added image quality filter (needs testing) 89 | 90 | ### 1.4.4 ### 91 | * Fixed migration from image field to not only return image ID 92 | * Fixed a js error in field settings caused by a change in class names in ACF 93 | 94 | ### 1.4.3 ### 95 | * Removed unused assets 96 | 97 | ### 1.4.2 ### 98 | * Improved migration from standard field to ACF crop field 99 | * Fixed and issue that caused warnings when options was not set 100 | * Fixed crop preview not showing correct crop position in some browsers 101 | * Improved error handling when server setup does not support image handling 102 | * Fixed original image data missing when saving to media library in v4 103 | * Improved error handling in v4 104 | 105 | ### 1.4.1 ### 106 | * Fixed issue with image not cropping in v4 107 | 108 | ### 1.4 ### 109 | * Fixed images with dot in the file name resulting in odd cropped image names 110 | * Fixed issues with php notices in v4 111 | * Fixed issues with broken image fields in v4 112 | * Temporarily fixed images smaller than preview size not being added (ACF bug) 113 | * Updated localization thanks to @tmconnect 114 | * Various tweaks and fixes by @tmconnect 115 | 116 | ### 1.3 ### 117 | * Updated to be compatible with original image field changes as of ACF Pro 5.0.8. IMPORTANT: As this is a quick fix to ensure compatability with the newest ACF PRO version it is not backwards compatible. If you are using ACF Pro 5.0.7 and below, please use version 1.2 of this add-on. 118 | 119 | ### 1.2 ### 120 | * Improved: Edit image is now working for most cropped image fields. 121 | * Fix: Wrong GUID for generated images that could cause issues when moving a site to a new location 122 | * Tweak: Added "original_image"-attribute when using return type "Object", containing the original image data. 123 | * Tweak: Return type "Object" is now available when not saving cropped image to media library. The data except from url, width and height is fetched from the original image. 124 | * Feature: It is now possible to hide cropped images from the media dialog. (See the new settings section) NB.: Only works for future cropped images. 125 | * Feature: Retina-mode, that makes the image field require and crop double the dimensions. Results in better integration with plugins like WP Retina 2x 126 | * Feature: Settings-seciton under Settings -> Media. Here you can choose to hide cropped images from the media dialog as well as enable/disable global retina mode. 127 | 128 | ### 1.1.4 ### 129 | * Fixed an issue causing a php warning when editing custom fields 130 | * Fixed a js-issue causing image-crop-field hiding all subfields when editing repeater-/flexible content-fields 131 | 132 | ### 1.1.3 ### 133 | * Fixed another issue with save to media option 134 | 135 | ### 1.1.2 ### 136 | * Fixed issue with force crop option 137 | * Fixed issue with save to media option 138 | * Fixed issue with return type object 139 | 140 | ### 1.1.1 ### 141 | * Removed unsued references and that caused php warnings 142 | * Added a missing default value that caused a php warning 143 | 144 | ### 1.1 ### 145 | * Added ACF5 compatibility. 146 | * Please report any compatibility issues. As this has been an urgent feature request I have not had as much time for testing as I would have liked. 147 | 148 | ### 1.0 ### 149 | * Added option to save the image to media library or refer directly to the created image, not using the media library. 150 | * Added better compatibility with the native image field making it possible to migrate from the regular image field to the crop-image field without losing the images currently attached. (It doesn't work the other way around) 151 | 152 | ### 0.8 ### 153 | * Fixed an issue resulting in a black image, when image was cropped without moving the crop handles 154 | 155 | ### 0.7 ### 156 | * Fixed return types other than image id causing fatal error 157 | 158 | ### 0.6 ### 159 | * Fix for WP installs with non-standard folder structures 160 | 161 | ### 0.5 ### 162 | * Initial Release. 163 | 164 | -------------------------------------------------------------------------------- /lang/acf-image_crop-de_DE.po: -------------------------------------------------------------------------------- 1 | msgid "" 2 | msgstr "" 3 | "Project-Id-Version: Advanced Custom Fields: Image Crop Add-on v1.3\n" 4 | "Report-Msgid-Bugs-To: \n" 5 | "POT-Creation-Date: \n" 6 | "PO-Revision-Date: 2014-09-13 13:45:02+0000\n" 7 | "Last-Translator: thomas \n" 8 | "Language-Team: \n" 9 | "MIME-Version: 1.0\n" 10 | "Content-Type: text/plain; charset=UTF-8\n" 11 | "Content-Transfer-Encoding: 8bit\n" 12 | "Plural-Forms: nplurals=2; plural=n != 1;\n" 13 | "X-Generator: CSL v1.x\n" 14 | "X-Poedit-Language: German\n" 15 | "X-Poedit-Country: GERMANY\n" 16 | "X-Poedit-SourceCharset: utf-8\n" 17 | "X-Poedit-KeywordsList: __;_e;__ngettext:1,2;_n:1,2;__ngettext_noop:1,2;_n_noop:1,2;_c,_nc:4c,1,2;_x:1,2c;_ex:1,2c;_nx:4c,1,2;_nx_noop:4c,1,2;\n" 18 | "X-Poedit-Basepath: \n" 19 | "X-Poedit-Bookmarks: \n" 20 | "X-Poedit-SearchPath-0: .\n" 21 | "X-Textdomain-Support: yes" 22 | 23 | #: acf-image-crop-v4.php:23 24 | #: acf-image-crop-v5.php:32 25 | #@ default 26 | #@ acf-image_crop 27 | msgid "Image with user-crop" 28 | msgstr "Bild (mit Größenoption)" 29 | 30 | #: acf-image-crop-v4.php:24 31 | #@ acf 32 | msgid "Content" 33 | msgstr "" 34 | 35 | #: acf-image-crop-v4.php:105 36 | #: acf-image-crop-v5.php:133 37 | #@ acf 38 | #@ acf-image_crop 39 | msgid "Crop type" 40 | msgstr "Art Beschnitt" 41 | 42 | #: acf-image-crop-v4.php:106 43 | #: acf-image-crop-v5.php:134 44 | #@ acf 45 | #@ acf-image_crop 46 | msgid "Select the type of crop the user should perform" 47 | msgstr "Auswahl, wie der Beschnitt angewendet wird" 48 | 49 | #: acf-image-crop-v4.php:117 50 | #: acf-image-crop-v5.php:140 51 | #@ acf 52 | #@ acf-image_crop 53 | msgid "Hard crop" 54 | msgstr "Absoluter Beschnitt" 55 | 56 | #: acf-image-crop-v4.php:118 57 | #: acf-image-crop-v5.php:141 58 | #@ acf 59 | #@ acf-image_crop 60 | msgid "Minimal dimensions" 61 | msgstr "Mindestgröße" 62 | 63 | #: acf-image-crop-v4.php:127 64 | #@ acf 65 | msgid "Target Size" 66 | msgstr "" 67 | 68 | #: acf-image-crop-v4.php:128 69 | #: acf-image-crop-v4.php:176 70 | #: acf-image-crop-v5.php:147 71 | #@ acf 72 | #@ acf-image_crop 73 | msgid "Select the target size for this field" 74 | msgstr "Definiert die Zielgröße des beschnittenen Bildes" 75 | 76 | #: acf-image-crop-v4.php:132 77 | #: acf-image-crop-v5.php:151 78 | #@ acf 79 | #@ acf-image_crop 80 | msgid "Custom size" 81 | msgstr "Benutzerdefiniert" 82 | 83 | #: acf-image-crop-v4.php:147 84 | #@ acf 85 | msgid "Dimensions" 86 | msgstr "" 87 | 88 | #: acf-image-crop-v4.php:148 89 | #@ acf 90 | msgid "Enter the dimensions for the image." 91 | msgstr "" 92 | 93 | #: acf-image-crop-v4.php:148 94 | #@ acf 95 | msgid "Enter the minimum dimensions for the image. Leave fields blank for no minimum requirement." 96 | msgstr "" 97 | 98 | #: acf-image-crop-v4.php:175 99 | #: acf-image-crop-v5.php:183 100 | #: image_crop-v3.php:130 101 | #@ acf 102 | msgid "Preview Size" 103 | msgstr "" 104 | 105 | #: acf-image-crop-v4.php:193 106 | #: acf-image-crop-v5.php:192 107 | #@ acf 108 | #@ acf-image_crop 109 | msgid "Force crop" 110 | msgstr "Beschnitt starten" 111 | 112 | #: acf-image-crop-v4.php:194 113 | #@ acf 114 | msgid "Force the user to crop the image as soon at it is selected." 115 | msgstr "" 116 | 117 | #: acf-image-crop-v4.php:204 118 | #: acf-image-crop-v4.php:224 119 | #: acf-image-crop-v4.php:251 120 | #: acf-image-crop-v5.php:197 121 | #: acf-image-crop-v5.php:208 122 | #: acf-image-crop-v5.php:219 123 | #@ acf 124 | msgid "Yes" 125 | msgstr "" 126 | 127 | #: acf-image-crop-v4.php:205 128 | #: acf-image-crop-v4.php:225 129 | #: acf-image-crop-v4.php:252 130 | #: acf-image-crop-v5.php:197 131 | #: acf-image-crop-v5.php:208 132 | #: acf-image-crop-v5.php:219 133 | #@ acf 134 | msgid "No" 135 | msgstr "" 136 | 137 | #: acf-image-crop-v4.php:213 138 | #@ acf 139 | msgid "Save cropped image in media library" 140 | msgstr "" 141 | 142 | #: acf-image-crop-v4.php:214 143 | #@ acf 144 | msgid "If the cropped image is not saved in the media library, the \"Image URL\" is the only available return value." 145 | msgstr "" 146 | 147 | #: acf-image-crop-v4.php:260 148 | #: acf-image-crop-v5.php:225 149 | #@ acf 150 | msgid "Return Value" 151 | msgstr "Rückgabewert" 152 | 153 | #: acf-image-crop-v4.php:261 154 | #: acf-image-crop-v5.php:226 155 | #@ acf 156 | msgid "Specify the returned value on front end" 157 | msgstr "Legt den Rückgabewert im Front-End fest" 158 | 159 | #: acf-image-crop-v4.php:271 160 | #: acf-image-crop-v5.php:233 161 | #@ acf 162 | msgid "Image URL" 163 | msgstr "" 164 | 165 | #: acf-image-crop-v4.php:272 166 | #: acf-image-crop-v5.php:234 167 | #@ acf 168 | msgid "Image ID" 169 | msgstr "" 170 | 171 | #: acf-image-crop-v4.php:273 172 | #@ acf 173 | msgid "Image Object" 174 | msgstr "" 175 | 176 | #: acf-image-crop-v4.php:370 177 | #@ acf 178 | msgid "Remove" 179 | msgstr "" 180 | 181 | #: acf-image-crop-v4.php:371 182 | #@ acf 183 | msgid "Edit" 184 | msgstr "" 185 | 186 | #: acf-image-crop-v4.php:397 187 | #: acf-image-crop-v5.php:381 188 | #@ acf 189 | msgid "No image selected" 190 | msgstr "" 191 | 192 | #: acf-image-crop-v4.php:397 193 | #: acf-image-crop-v5.php:381 194 | #@ acf 195 | msgid "Add Image" 196 | msgstr "" 197 | 198 | #: acf-image-crop-v5.php:153 199 | #@ acf-image_crop 200 | msgid "Target size" 201 | msgstr "Zielgröße" 202 | 203 | #: acf-image-crop-v5.php:163 204 | #@ acf-image_crop 205 | msgid "Custom target width" 206 | msgstr "Benutzerdefinierte Breite" 207 | 208 | #: acf-image-crop-v5.php:164 209 | #: acf-image-crop-v5.php:174 210 | #@ acf-image_crop 211 | msgid "Leave blank for no restriction (does not work with hard crop option)" 212 | msgstr "Leer lassen für keine Einschränkung (\"Absoluter Beschnitt\" erfordert eine Eingabe)" 213 | 214 | #: acf-image-crop-v5.php:173 215 | #@ acf-image_crop 216 | msgid "Custom target height" 217 | msgstr "Benutzerdefinierte Höhe" 218 | 219 | #: acf-image-crop-v5.php:193 220 | #@ acf-image_crop 221 | msgid "Force the user to crop the image as soon at it is selected" 222 | msgstr "Öffnet das Bearbeitungsfenster für den Beschnitt, sobald das Bild ausgewählt ist." 223 | 224 | #: acf-image-crop-v5.php:202 225 | #@ acf-image_crop 226 | msgid "Save cropped image to media library" 227 | msgstr "Speichere beschnittenes Bild in der Mediathek" 228 | 229 | #: acf-image-crop-v5.php:203 230 | #@ acf-image_crop 231 | msgid "If the cropped image is not saved in the media library, \"Image URL\" is the only available return value." 232 | msgstr "Wenn das beschnittene Bild nicht in der Mediathek gespeichert wird, steht nur \"Bild URL\" als Rückgabewert zur Verfügung" 233 | 234 | #: acf-image-crop-v5.php:240 235 | #@ acf 236 | msgid "Library" 237 | msgstr "" 238 | 239 | #: acf-image-crop-v5.php:241 240 | #@ acf 241 | msgid "Limit the media library choice" 242 | msgstr "" 243 | 244 | #: acf-image-crop-v5.php:246 245 | #@ acf 246 | msgid "All" 247 | msgstr "" 248 | 249 | #: acf-image-crop-v5.php:247 250 | #@ acf 251 | msgid "Uploaded to post" 252 | msgstr "" 253 | 254 | #: acf-image-crop-v5.php:76 255 | #@ acf-image_crop 256 | msgid "Width should be at least: " 257 | msgstr "Breite mindestens: " 258 | 259 | #: acf-image-crop-v5.php:77 260 | #@ acf-image_crop 261 | msgid "Height should be at least: " 262 | msgstr "Höhe mindestens: " 263 | 264 | #: acf-image-crop-v5.php:78 265 | #@ acf-image_crop 266 | msgid "Selected image width: " 267 | msgstr "Breite ausgewähltes Bild: " 268 | 269 | #: acf-image-crop-v5.php:79 270 | #@ acf-image_crop 271 | msgid "Selected image height: " 272 | msgstr "Höhe ausgewähltes Bild: " 273 | 274 | #: acf-image-crop-v5.php:80 275 | #@ acf-image_crop 276 | msgid "Warning: The selected image is smaller than the required size!" 277 | msgstr "Warnung: Das ausgewählte Bild ist kleiner als die geforderte Mindestgröße!" 278 | 279 | #: image_crop-v3.php:30 280 | #@ default 281 | msgid "Image - Custom crop" 282 | msgstr "" 283 | 284 | #: image_crop-v3.php:131 285 | #@ acf 286 | msgid "Thumbnail is advised" 287 | msgstr "" 288 | 289 | #: image_crop-v3.php:142 290 | #@ default 291 | msgid "Thumbnail" 292 | msgstr "" 293 | 294 | #: image_crop-v3.php:143 295 | #@ default 296 | msgid "Something Else" 297 | msgstr "" 298 | 299 | #: acf-image-crop-v5.php:232 300 | #@ acf 301 | msgid "Image Array" 302 | msgstr "" 303 | 304 | #: acf-image-crop-v4.php:889 305 | #: acf-image-crop-v5.php:661 306 | #@ acf-image_crop 307 | msgid "ACF Image Crop Settings" 308 | msgstr "ACF Image Crop Einstellungen" 309 | 310 | #: acf-image-crop-v4.php:901 311 | #: acf-image-crop-v5.php:674 312 | #@ acf-image_crop 313 | msgid "Hide cropped images from media dialog" 314 | msgstr "Beschnittene Bilder in Medienübersicht ausblenden" 315 | 316 | #: acf-image-crop-v4.php:233 317 | #: acf-image-crop-v5.php:215 318 | #@ acf-image_crop 319 | msgid "Require and crop double the size set for this image. Enable this if you are using plugins like WP Retina 2x." 320 | msgstr "Retina Modus aktivieren - Der Anwender muss ein Bild in doppelter Größe auswählen. Diese Option sollte aktiviert werden, wenn ein Plugin wie z.B. WP Retina 2x aktiviert ist." 321 | 322 | #: acf-image-crop-v4.php:240 323 | #: acf-image-crop-v5.php:214 324 | #@ acf-image_crop 325 | msgid "Retina/@2x mode " 326 | msgstr "Retina/@2x Modus" 327 | 328 | #: acf-image-crop-v4.php:909 329 | #: acf-image-crop-v5.php:682 330 | #@ acf-image_crop 331 | msgid "Enable global retina mode (beta)" 332 | msgstr "Globalen Retina Modus (beta) aktivieren" 333 | 334 | #: acf-image-crop-v5.php:149 335 | #@ acf-image_crop 336 | msgid "Retina mode is enabled - user will be required to select an image twice this size" 337 | msgstr "Retina Modus ist aktiviert - Der Anwender muss ein Bild in doppelter Größe auswählen" 338 | 339 | #: acf-image-crop-v5.php:184 340 | #@ acf 341 | msgid "Shown when entering data" 342 | msgstr "" 343 | 344 | #: acf-image-crop-v5.php:357 345 | #@ acf-image_crop 346 | msgid "Crop the image" 347 | msgstr "Bildauschnitt wählen" 348 | 349 | #: acf-image-crop-v5.php:365 350 | #@ acf-image_crop 351 | msgid "Preview" 352 | msgstr "Vorschau" 353 | 354 | #: acf-image-crop-v5.php:370 355 | #@ acf-image_crop 356 | msgid "Cancel" 357 | msgstr "Abbrechen" 358 | 359 | #: acf-image-crop-v5.php:370 360 | #@ acf-image_crop 361 | msgid "Crop!" 362 | msgstr "Beschneiden" 363 | 364 | #: acf-image-crop-v5.php:376 365 | #@ acf-image_crop 366 | msgid "Crop" 367 | msgstr "Beschneiden" 368 | 369 | -------------------------------------------------------------------------------- /image_crop-v3.php: -------------------------------------------------------------------------------- 1 | name = 'image_crop'; 30 | $this->title = __('Image - Custom crop'); 31 | $this->defaults = array( 32 | // add default here to merge into your field. 33 | // This makes life easy when creating the field options as you don't need to use any if( isset('') ) logic. eg: 34 | //'preview_size' => 'thumbnail' 35 | ); 36 | 37 | // settings 38 | $this->settings = array( 39 | 'path' => $this->helpers_get_path(__FILE__), 40 | 'dir' => $this->helpers_get_dir(__FILE__), 41 | 'version' => '1.0.0' 42 | ); 43 | 44 | } 45 | 46 | 47 | /* 48 | * helpers_get_path 49 | * 50 | * @description: calculates the path (works for plugin / theme folders) 51 | * @since: 3.6 52 | * @created: 30/01/13 53 | */ 54 | 55 | function helpers_get_path($file) 56 | { 57 | return trailingslashit(dirname($file)); 58 | } 59 | 60 | 61 | /* 62 | * helpers_get_dir 63 | * 64 | * @description: calculates the directory (works for plugin / theme folders) 65 | * @since: 3.6 66 | * @created: 30/01/13 67 | */ 68 | 69 | function helpers_get_dir($file) 70 | { 71 | $dir = trailingslashit(dirname($file)); 72 | $count = 0; 73 | 74 | 75 | // sanitize for Win32 installs 76 | $dir = str_replace('\\', '/', $dir); 77 | 78 | 79 | // if file is in plugins folder 80 | $wp_plugin_dir = str_replace('\\', '/', WP_PLUGIN_DIR); 81 | $dir = str_replace($wp_plugin_dir, WP_PLUGIN_URL, $dir, $count); 82 | 83 | 84 | if($count < 1) 85 | { 86 | // if file is in wp-content folder 87 | $wp_content_dir = str_replace('\\', '/', WP_CONTENT_DIR); 88 | $dir = str_replace($wp_content_dir, WP_CONTENT_URL, $dir, $count); 89 | } 90 | 91 | 92 | if($count < 1) 93 | { 94 | // if file is in ??? folder 95 | $wp_dir = str_replace('\\', '/', ABSPATH); 96 | $dir = str_replace($wp_dir, site_url('/'), $dir); 97 | } 98 | 99 | return $dir; 100 | } 101 | 102 | 103 | /*-------------------------------------------------------------------------------------- 104 | * 105 | * create_options 106 | * - this function is called from core/field_meta_box.php to create extra options 107 | * for your field 108 | * 109 | * @params 110 | * - $key (int) - the $_POST obejct key required to save the options to the field 111 | * - $field (array) - the field object 112 | * 113 | * @author Elliot Condon 114 | * @since 2.2.0 115 | * 116 | *-------------------------------------------------------------------------------------*/ 117 | 118 | function create_options($key, $field) 119 | { 120 | // defaults? 121 | /* 122 | $field = array_merge($this->defaults, $field); 123 | */ 124 | 125 | 126 | // Create Field Options HTML 127 | ?> 128 | 129 | 130 | 131 |

132 | 133 | 134 | parent->create_field(array( 137 | 'type' => 'radio', 138 | 'name' => 'fields[' . $key . '][preview_size]', 139 | 'value' => $field['preview_size'], 140 | 'layout' => 'horizontal', 141 | 'choices' => array( 142 | 'thumbnail' => __('Thumbnail'), 143 | 'something_else' => __('Something Else'), 144 | ) 145 | )); 146 | 147 | ?> 148 | 149 | 150 | defaults, $field); 190 | */ 191 | 192 | // perhaps use $field['preview_size'] to alter the markup? 193 | 194 | 195 | // create Field HTML 196 | ?> 197 |
198 | 199 |
200 | settings['dir'] . 'js/input.js', array('acf-input'), $this->settings['version']); 241 | 242 | // scripts 243 | wp_enqueue_script(array( 244 | 'acf-input-image_crop', 245 | )); 246 | 247 | 248 | } 249 | 250 | function admin_print_styles() 251 | { 252 | // Note: This function can be removed if not used 253 | 254 | 255 | wp_register_style('acf-input-image_crop', $this->settings['dir'] . 'css/input.css', array('acf-input'), $this->settings['version']); 256 | 257 | // styles 258 | wp_enqueue_style(array( 259 | 'acf-input-image_crop', 260 | )); 261 | } 262 | 263 | 264 | /*-------------------------------------------------------------------------------------- 265 | * 266 | * update_value 267 | * - this function is called when saving a post object that your field is assigned to. 268 | * the function will pass through the 3 parameters for you to use. 269 | * 270 | * @params 271 | * - $post_id (int) - usefull if you need to save extra data or manipulate the current 272 | * post object 273 | * - $field (array) - usefull if you need to manipulate the $value based on a field option 274 | * - $value (mixed) - the new value of your field. 275 | * 276 | * @author Elliot Condon 277 | * @since 2.2.0 278 | * 279 | *-------------------------------------------------------------------------------------*/ 280 | 281 | function update_value($post_id, $field, $value) 282 | { 283 | // Note: This function can be removed if not used 284 | 285 | // do stuff with value 286 | 287 | // save value 288 | parent::update_value($post_id, $field, $value); 289 | } 290 | 291 | 292 | /*-------------------------------------------------------------------------------------- 293 | * 294 | * get_value 295 | * - called from the edit page to get the value of your field. This function is useful 296 | * if your field needs to collect extra data for your create_field() function. 297 | * 298 | * @params 299 | * - $post_id (int) - the post ID which your value is attached to 300 | * - $field (array) - the field object. 301 | * 302 | * @author Elliot Condon 303 | * @since 2.2.0 304 | * 305 | *-------------------------------------------------------------------------------------*/ 306 | 307 | function get_value($post_id, $field) 308 | { 309 | // Note: This function can be removed if not used 310 | 311 | // get value 312 | $value = parent::get_value($post_id, $field); 313 | 314 | // format value 315 | 316 | // return value 317 | return $value; 318 | } 319 | 320 | 321 | /*-------------------------------------------------------------------------------------- 322 | * 323 | * get_value_for_api 324 | * - called from your template file when using the API functions (get_field, etc). 325 | * This function is useful if your field needs to format the returned value 326 | * 327 | * @params 328 | * - $post_id (int) - the post ID which your value is attached to 329 | * - $field (array) - the field object. 330 | * 331 | * @author Elliot Condon 332 | * @since 3.0.0 333 | * 334 | *-------------------------------------------------------------------------------------*/ 335 | 336 | function get_value_for_api($post_id, $field) 337 | { 338 | // Note: This function can be removed if not used 339 | 340 | // get value 341 | $value = $this->get_value($post_id, $field); 342 | 343 | // format value 344 | 345 | // return value 346 | return $value; 347 | 348 | } 349 | 350 | } 351 | 352 | ?> 353 | -------------------------------------------------------------------------------- /js/input-v4.js: -------------------------------------------------------------------------------- 1 | (function($){ 2 | 3 | 4 | /* 5 | * acf/setup_fields 6 | * 7 | * This event is triggered when ACF adds any new elements to the DOM. 8 | * 9 | * @type function 10 | * @since 1.0.0 11 | * @date 01/01/12 12 | * 13 | * @param event e: an event object. This can be ignored 14 | * @param Element postbox: An element which contains the new HTML 15 | * 16 | * @return N/A 17 | */ 18 | 19 | $(document).on('acf/setup_fields', function(e, postbox){ 20 | $(postbox).find('.field_type-image_crop').each(function(){ 21 | var $field = $(this).find('.acf-image-crop'); 22 | var $options = $field; 23 | // var $el = $field.find('.acf-image-crop'); 24 | $field.find('.acf-image-value').on('change', function(){ 25 | var originalImage = $(this).val(); 26 | if($(this).val()){ 27 | $field.removeClass('invalid'); 28 | $field.find('.init-crop-button').removeAttr('disabled'); 29 | $field.find('.acf-image-value').data('original-image', originalImage); 30 | $field.find('.acf-image-value').data('cropped-image', originalImage); 31 | $field.find('.acf-image-value').data('cropped', false); 32 | $.post(ajaxurl, {action: 'acf_image_crop_get_image_size', image_id: originalImage}, function(data, textStatus, xhr) { 33 | if($field.find('img.crop-image').length == 0){ 34 | $field.find('.crop-action').append($('')); 35 | } 36 | $field.find('img.crop-image').attr('src', data['url']); 37 | $field.find('img.crop-image').data('width', data['width']); 38 | $field.find('img.crop-image').data('height', data['height']); 39 | var warnings = []; 40 | var valid = true; 41 | if($options.data('width') && data['width'] < $options.data('width')){ 42 | warnings.push('Width should be at least: ' + $options.data('width') + 'px (Selected image width: ' + data['width'] + 'px)'); 43 | valid = false; 44 | } 45 | if($options.data('height') && data['height'] < $options.data('height')){ 46 | warnings.push('Height should be at least: ' + $options.data('height') + 'px (Selected image height: ' + data['height'] + 'px)'); 47 | valid = false; 48 | } 49 | if(!valid){ 50 | $field.addClass('invalid'); 51 | $field.find('.init-crop-button').attr('disabled', 'disabled'); 52 | alert('Warning: The selected image is smaller than the required size:\n' + warnings.join('\n')); 53 | } 54 | else{ 55 | if($options.data('force-crop')){ 56 | initCrop($field); 57 | } 58 | } 59 | 60 | }, 'json'); 61 | updateFieldValue($field); 62 | } 63 | else{ 64 | //Do nothing 65 | } 66 | 67 | }) 68 | $field.find('.init-crop-button').click(function(e){ 69 | e.preventDefault(); 70 | initCrop($field); 71 | }); 72 | $field.find('.perform-crop-button').click(function(e){ 73 | e.preventDefault(); 74 | performCrop($field); 75 | }); 76 | $field.find('.cancel-crop-button').click(function(e){ 77 | e.preventDefault(); 78 | cancelCrop($field); 79 | }); 80 | $field.on('click', '.acf-image-uploader .acf-button-edit', function( e ){ 81 | e.preventDefault(); 82 | e.stopPropagation(); 83 | var id = $field.find('.acf-image-value').data('cropped-image'); 84 | if(!$.isNumeric(id)){ 85 | id = $field.find('.acf-image-value').data('original-image');; 86 | } 87 | acf.fields.image_crop.set({ $el : $(this).closest('.acf-image-uploader') }).edit(id); 88 | }); 89 | // $field.find('[data-name=edit-button]').click(function(e){ 90 | // e.preventDefault(); 91 | // e.stopPropagation(); 92 | // var id = $field.find('.acf-image-value').data('cropped-image'); 93 | // if(!$.isNumeric(id)){ 94 | // id = $field.find('.acf-image-value').data('original-image');; 95 | // } 96 | // //acf.fields.image_crop.edits(id); 97 | //}); 98 | }); 99 | 100 | }); 101 | 102 | function initCrop($field){ 103 | var $options = $field; 104 | var options = { 105 | handles: true, 106 | onSelectEnd: function (img, selection) { 107 | updateThumbnail($field, img, selection); 108 | updateCropData($field, img, selection); 109 | }, 110 | imageWidth:$options.find('.crop-stage img.crop-image').data('width'), 111 | imageHeight:$options.find('.crop-stage img.crop-image').data('height'), 112 | x1: 0, 113 | y1: 0 114 | }; 115 | if($options.data('crop-type') == 'hard'){ 116 | options.aspectRatio = $options.data('width') + ':' + $options.data('height'); 117 | options.minWidth = $options.data('width'); 118 | options.minHeight = $options.data('height'); 119 | options.x2 = $options.data('width'); 120 | options.y2 = $options.data('height'); 121 | } 122 | else if($options.data('crop-type') == 'min'){ 123 | if($options.data('width')){ 124 | options.minWidth = $options.data('width'); 125 | options.x2 = $options.data('width'); 126 | } 127 | else{ 128 | options.x2 = options.imageWidth; 129 | } 130 | if($options.data('height')){ 131 | options.minHeight = $options.data('height'); 132 | options.y2 = $options.data('height'); 133 | } 134 | else{ 135 | options.y2 = options.imageHeight; 136 | } 137 | } 138 | // Center crop - disabled needs more testing 139 | // options.x1 = options.imageWidth/2 - (options.minWidth/2); 140 | // options.y1 = options.imageHeight/2 - (options.minHeight/2) 141 | // options.x2 = options.minWidth + options.x1; 142 | // options.y2 = options.minHeight + options.y1; 143 | //options.y1 = (options.imageHeight - options.minHeight) / 2; 144 | if(!$field.hasClass('invalid')){ 145 | toggleCropView($field); 146 | $field.find('.crop-stage img.crop-image').imgAreaSelect(options); 147 | updateCropData($field, $field.find('.crop-stage img.crop-image').get(0), {y1: options.y1, y2: options.y2, x1: options.x1, x2: options.x2}); 148 | updateThumbnail($field, $field.find('.crop-stage img.crop-image').get(0), {y1: options.y1, y2: options.y2, x1: options.x1, x2: options.x2}); 149 | } 150 | } 151 | 152 | function updateCropData($field, img, selection){ 153 | var $options = $field; 154 | $options.data('x1', selection.x1); 155 | $options.data('x2', selection.x2); 156 | $options.data('y1', selection.y1); 157 | $options.data('y2', selection.y2); 158 | } 159 | 160 | function updateThumbnail($field, img, selection){ 161 | var $options = $field; 162 | var div = $field.find('.crop-preview .preview'); 163 | var targetWidth = $field.find('.crop-preview .preview').width(); 164 | var factor = targetWidth / (selection.x2 - selection.x1); 165 | //image 166 | div.css('background-image', 'url(' + img.src + ')'); 167 | //width 168 | div.css('width', (selection.x2 - selection.x1) * factor); 169 | //height 170 | div.css('height', (selection.y2 - selection.y1) * factor); 171 | 172 | // Set offset - Fix by @christdg 173 | pos_x = 0-(selection.x1 * factor); 174 | pos_y = 0-(selection.y1 * factor); 175 | div.css('background-position', pos_x + 'px ' + pos_y + 'px'); 176 | 177 | div.css('background-size', $options.find('.crop-stage img.crop-image').data('width') * factor + 'px' + ' ' + $options.find('.crop-stage img.crop-image').data('height') * factor + 'px'); 178 | } 179 | 180 | function generateCropJSON(originalImage, croppedImage){ 181 | var obj = { 182 | original_image: originalImage, 183 | cropped_image: croppedImage 184 | } 185 | return JSON.stringify(obj); 186 | } 187 | 188 | function performCrop($field){ 189 | if(!$field.find('.crop-stage').hasClass('loading')){ 190 | $field.find('.crop-stage').addClass('loading'); 191 | var $options = $field; 192 | var targetWidth = $options.data('width'); 193 | var targetHeight = $options.data('height'); 194 | var saveToMediaLibrary = $options.data('save-to-media-library'); 195 | if($options.data('crop-type') == 'min'){ 196 | targetWidth = $options.data('x2') - $options.data('x1'); 197 | targetHeight = $options.data('y2') - $options.data('y1'); 198 | } 199 | var data = { 200 | action: 'acf_image_crop_perform_crop', 201 | id: $field.find('.acf-image-value').data('original-image'), 202 | x1: $options.data('x1'), 203 | x2: $options.data('x2'), 204 | y1: $options.data('y1'), 205 | y2: $options.data('y2'), 206 | target_width: targetWidth, 207 | target_height: targetHeight, 208 | preview_size: $options.data('preview_size'), 209 | save_to_media_library: saveToMediaLibrary 210 | } 211 | $.post(ajaxurl, data, function(data, textStatus, xhr) { 212 | if(data.success){ 213 | $field.find('.acf-image-image').attr('src', data.preview_url); 214 | $field.find('.acf-image-value').data('cropped-image', data.value); 215 | $field.find('.acf-image-value').data('cropped', true); 216 | updateFieldValue($field); 217 | } 218 | else{ 219 | $field.append('

Sorry, an error occurred when trying to crop your image:

' + data.error_message); 220 | } 221 | $field.find('.crop-stage').removeClass('loading'); 222 | cancelCrop($field); 223 | }, 'json'); 224 | } 225 | } 226 | 227 | function cancelCrop($field){ 228 | toggleCropView($field); 229 | $field.find('.crop-stage img.crop-image').imgAreaSelect({remove:true}); 230 | } 231 | 232 | function toggleCropView($field){ 233 | if($field.hasClass('cropping')){ 234 | $('#acf-image-crop-overlay').remove(); 235 | } 236 | else{ 237 | $('body').append($('
')); 238 | } 239 | $field.toggleClass('cropping'); 240 | } 241 | 242 | function updateFieldValue($field){ 243 | var $input = $field.find('.acf-image-value'); 244 | $input.val(generateCropJSON($input.data('original-image'), $input.data('cropped-image'))); 245 | } 246 | 247 | function getFullImageUrl(id, callback){ 248 | $.post(ajaxurl, {images: []}, function(data, textStatus, xhr) { 249 | }, 'json'); 250 | } 251 | 252 | 253 | // reference 254 | var _media = acf.media; 255 | 256 | 257 | acf.fields.image_crop = { 258 | 259 | $el : null, 260 | $input : null, 261 | 262 | o : {}, 263 | 264 | set : function( o ){ 265 | 266 | // merge in new option 267 | $.extend( this, o ); 268 | 269 | 270 | // find input 271 | this.$input = this.$el.find('input[type="hidden"]'); 272 | 273 | 274 | // get options 275 | this.o = acf.helpers.get_atts( this.$el ); 276 | 277 | 278 | // multiple? 279 | this.o.multiple = this.$el.closest('.repeater').exists() ? true : false; 280 | 281 | 282 | // wp library query 283 | this.o.query = { 284 | type : 'image' 285 | }; 286 | 287 | 288 | // library 289 | if( this.o.library == 'uploadedTo' ) 290 | { 291 | this.o.query.uploadedTo = acf.o.post_id; 292 | } 293 | 294 | 295 | // return this for chaining 296 | return this; 297 | 298 | }, 299 | init : function(){ 300 | 301 | // is clone field? 302 | if( acf.helpers.is_clone_field(this.$input) ) 303 | { 304 | return; 305 | } 306 | 307 | }, 308 | add : function( image ){ 309 | 310 | // this function must reference a global div variable due to the pre WP 3.5 uploader 311 | // vars 312 | var div = _media.div; 313 | 314 | 315 | // set atts 316 | div.find('.acf-image-image').attr( 'src', image.url ); 317 | div.find('.acf-image-value').val( image.id ).trigger('change'); 318 | 319 | 320 | // set div class 321 | div.addClass('active'); 322 | 323 | 324 | // validation 325 | div.closest('.field').removeClass('error'); 326 | 327 | }, 328 | edit : function(id){ 329 | 330 | 331 | 332 | // set global var 333 | _media.div = this.$el; 334 | 335 | 336 | // clear the frame 337 | _media.clear_frame(); 338 | 339 | 340 | // create the media frame 341 | _media.frame = wp.media({ 342 | title : acf.l10n.image.edit, 343 | multiple : false, 344 | button : { text : acf.l10n.image.update } 345 | }); 346 | 347 | 348 | // log events 349 | /* 350 | acf.media.frame.on('all', function(e){ 351 | 352 | console.log( e ); 353 | 354 | }); 355 | */ 356 | 357 | // open 358 | _media.frame.on('open',function() { 359 | 360 | // set to browse 361 | if( _media.frame.content._mode != 'browse' ) 362 | { 363 | _media.frame.content.mode('browse'); 364 | } 365 | 366 | // add class 367 | _media.frame.$el.closest('.media-modal').addClass('acf-media-modal acf-expanded'); 368 | 369 | 370 | // set selection 371 | var selection = _media.frame.state().get('selection'), 372 | attachment = wp.media.attachment( id ); 373 | 374 | 375 | // to fetch or not to fetch 376 | if( $.isEmptyObject(attachment.changed) ) 377 | { 378 | attachment.fetch(); 379 | } 380 | 381 | selection.add( attachment ); 382 | }); 383 | 384 | 385 | // close 386 | _media.frame.on('close',function(){ 387 | 388 | // remove class 389 | _media.frame.$el.closest('.media-modal').removeClass('acf-media-modal'); 390 | 391 | }); 392 | 393 | 394 | // Finally, open the modal 395 | acf.media.frame.open(); 396 | 397 | }, 398 | remove : function() 399 | { 400 | 401 | // set atts 402 | this.$el.find('.acf-image-image').attr( 'src', '' ); 403 | this.$el.find('.acf-image-value').val( '' ).trigger('change'); 404 | 405 | 406 | // remove class 407 | this.$el.removeClass('active'); 408 | 409 | }, 410 | popup : function() 411 | { 412 | // reference 413 | var t = this; 414 | 415 | // set global var 416 | _media.div = this.$el; 417 | 418 | 419 | // clear the frame 420 | _media.clear_frame(); 421 | 422 | 423 | // Create the media frame 424 | _media.frame = wp.media({ 425 | states : [ 426 | new wp.media.controller.Library({ 427 | library : wp.media.query( t.o.query ), 428 | multiple : t.o.multiple, 429 | title : acf.l10n.image.select, 430 | priority : 20, 431 | filterable : 'all' 432 | }) 433 | ] 434 | }); 435 | 436 | 437 | /*acf.media.frame.on('all', function(e){ 438 | 439 | console.log( e ); 440 | 441 | });*/ 442 | 443 | 444 | // customize model / view 445 | acf.media.frame.on('content:activate', function(){ 446 | // vars 447 | var toolbar = null, 448 | filters = null; 449 | 450 | 451 | // populate above vars making sure to allow for failure 452 | try 453 | { 454 | toolbar = acf.media.frame.content.get().toolbar; 455 | filters = toolbar.get('filters'); 456 | } 457 | catch(e) 458 | { 459 | // one of the objects was 'undefined'... perhaps the frame open is Upload Files 460 | //console.log( e ); 461 | } 462 | 463 | 464 | // validate 465 | if( !filters ) 466 | { 467 | return false; 468 | } 469 | 470 | 471 | // filter only images 472 | $.each( filters.filters, function( k, v ){ 473 | 474 | v.props.type = 'image'; 475 | 476 | }); 477 | 478 | 479 | // no need for 'uploaded' filter 480 | if( t.o.library == 'uploadedTo' ) 481 | { 482 | filters.$el.find('option[value="uploaded"]').remove(); 483 | filters.$el.after('' + acf.l10n.image.uploadedTo + '') 484 | 485 | $.each( filters.filters, function( k, v ){ 486 | 487 | v.props.uploadedTo = acf.o.post_id; 488 | 489 | }); 490 | } 491 | 492 | 493 | // remove non image options from filter list 494 | filters.$el.find('option').each(function(){ 495 | 496 | // vars 497 | var v = $(this).attr('value'); 498 | 499 | 500 | // don't remove the 'uploadedTo' if the library option is 'all' 501 | if( v == 'uploaded' && t.o.library == 'all' ) 502 | { 503 | return; 504 | } 505 | 506 | if( v.indexOf('image') === -1 ) 507 | { 508 | $(this).remove(); 509 | } 510 | 511 | }); 512 | 513 | 514 | // set default filter 515 | filters.$el.val('image').trigger('change'); 516 | 517 | }); 518 | 519 | 520 | // When an image is selected, run a callback. 521 | acf.media.frame.on( 'select', function() { 522 | 523 | // get selected images 524 | selection = _media.frame.state().get('selection'); 525 | 526 | if( selection ) 527 | { 528 | var i = 0; 529 | 530 | selection.each(function(attachment){ 531 | 532 | // counter 533 | i++; 534 | 535 | 536 | // select / add another image field? 537 | if( i > 1 ) 538 | { 539 | // vars 540 | var $td = _media.div.closest('td'), 541 | $tr = $td.closest('.row'), 542 | $repeater = $tr.closest('.repeater'), 543 | key = $td.attr('data-field_key'), 544 | selector = 'td .acf-image-uploader:first'; 545 | 546 | 547 | // key only exists for repeater v1.0.1 + 548 | if( key ) 549 | { 550 | selector = 'td[data-field_key="' + key + '"] .acf-image-uploader'; 551 | } 552 | 553 | 554 | // add row? 555 | if( ! $tr.next('.row').exists() ) 556 | { 557 | $repeater.find('.add-row-end').trigger('click'); 558 | 559 | } 560 | 561 | 562 | // update current div 563 | _media.div = $tr.next('.row').find( selector ); 564 | 565 | } 566 | 567 | 568 | // vars 569 | var image = { 570 | id : attachment.id, 571 | url : attachment.attributes.url 572 | }; 573 | 574 | // is preview size available? 575 | if( attachment.attributes.sizes && attachment.attributes.sizes[ t.o.preview_size ] ) 576 | { 577 | image.url = attachment.attributes.sizes[ t.o.preview_size ].url; 578 | } 579 | 580 | // add image to field 581 | acf.fields.image.add( image ); 582 | 583 | 584 | }); 585 | // selection.each(function(attachment){ 586 | } 587 | // if( selection ) 588 | 589 | }); 590 | // acf.media.frame.on( 'select', function() { 591 | 592 | 593 | // Finally, open the modal 594 | acf.media.frame.open(); 595 | 596 | 597 | return false; 598 | }, 599 | 600 | // temporary gallery fix 601 | text : { 602 | title_add : "Select Image", 603 | title_edit : "Edit Image" 604 | } 605 | 606 | }; 607 | 608 | })(jQuery); 609 | -------------------------------------------------------------------------------- /js/input-old.js: -------------------------------------------------------------------------------- 1 | (function($){ 2 | acf.fields.image_crop = acf.field.extend({ 3 | 4 | type: 'image_crop', 5 | $el: null, 6 | 7 | events: { 8 | 'click [data-name="add"]': 'add', 9 | 'click [data-name="edit"]': 'edit', 10 | 'click [data-name="remove"]': 'remove', 11 | }, 12 | 13 | focus: function(){ 14 | 15 | this.$el = this.$field.find('.acf-image-uploader'); 16 | 17 | this.settings = acf.get_data( this.$el ); 18 | 19 | }, 20 | 21 | add: function() { 22 | 23 | // reference 24 | var self = this; 25 | 26 | 27 | // vars 28 | var field_key = acf.get_data( this.$field, 'key' ); 29 | 30 | 31 | // get repeater 32 | var $repeater = acf.get_closest_field( this.$field, {type:'repeater'} ); 33 | 34 | 35 | // popup 36 | var frame = acf.media.popup({ 37 | 'title' : acf._e('image', 'select'), 38 | 'mode' : 'select', 39 | 'type' : 'image', 40 | 'multiple' : $repeater.exists(), 41 | 'library' : this.settings.library, 42 | 'select' : function( attachment, i ) { 43 | 44 | // select / add another image field? 45 | if( i > 0 ) { 46 | 47 | // vars 48 | var $tr = self.$field.parent(), 49 | $next = false; 50 | 51 | 52 | // find next image field 53 | $tr.nextAll('.acf-row').not('.clone').each(function(){ 54 | 55 | // get next $field 56 | $next = acf.get_field( field_key, $(this) ); 57 | 58 | 59 | // bail early if $next was not found 60 | if( !$next ) { 61 | 62 | return; 63 | 64 | } 65 | 66 | 67 | // bail early if next file uploader has value 68 | if( $next.find('.acf-image-uploader.has-value').exists() ) { 69 | 70 | $next = false; 71 | return; 72 | 73 | } 74 | 75 | 76 | // end loop if $next is found 77 | return false; 78 | 79 | }); 80 | 81 | 82 | // add extra row if next is not found 83 | if( !$next ) { 84 | 85 | $tr = acf.fields.repeater.doFocus( $repeater ).add(); 86 | 87 | 88 | // bail early if no $tr (maximum rows hit) 89 | if( !$tr ) { 90 | 91 | return false; 92 | 93 | } 94 | 95 | 96 | // get next $field 97 | $next = acf.get_field( field_key, $tr ); 98 | 99 | } 100 | 101 | 102 | // update $el 103 | self.doFocus( $next ); 104 | 105 | } 106 | 107 | 108 | // add file to field 109 | self.render( attachment ); 110 | 111 | } 112 | }); 113 | 114 | 115 | }, 116 | 117 | render: function( attachment ){ 118 | 119 | // override url 120 | if( acf.isset(attachment, 'attributes', 'sizes', this.settings.preview_size, 'url') ) { 121 | 122 | attachment.url = attachment.attributes.sizes[ this.settings.preview_size ].url; 123 | 124 | } 125 | // set url to default url in case preview size does not exist 126 | else if(acf.isset(attachment, 'attributes', 'url')){ 127 | 128 | attachment.url = attachment.attributes.url; 129 | } 130 | 131 | // set atts 132 | this.$el.find('[data-name="image"]').attr( 'src', attachment.url ); 133 | this.$el.find('[data-name="id"]').val( attachment.id ).trigger('change'); 134 | 135 | 136 | // set div class 137 | this.$el.addClass('has-value'); 138 | 139 | }, 140 | 141 | edit: function() { 142 | 143 | // reference 144 | var self = this; 145 | 146 | 147 | // vars 148 | var id = this.$el.find('[data-name="id"]').val(); 149 | 150 | 151 | // popup 152 | var frame = acf.media.popup({ 153 | 154 | title: acf._e('image', 'edit'), 155 | button: acf._e('image', 'update'), 156 | mode: 'edit', 157 | id: id, 158 | 159 | select: function( attachment, i ) { 160 | 161 | // add file to field 162 | self.render( attachment ); 163 | 164 | } 165 | 166 | }); 167 | 168 | }, 169 | 170 | remove: function() { 171 | 172 | // vars 173 | var attachment = { 174 | id: '', 175 | url: '' 176 | }; 177 | 178 | 179 | // add file to field 180 | this.render( attachment ); 181 | 182 | 183 | // remove class 184 | this.$el.removeClass('has-value'); 185 | 186 | } 187 | 188 | }); 189 | 190 | function initialize_field( $el ) { 191 | var $field = $el, $options = $el.find('.acf-image-uploader'); 192 | $field.find('.acf-image-value').on('change', function(){ 193 | var originalImage = $(this).val(); 194 | if($(this).val()){ 195 | $field.removeClass('invalid'); 196 | $field.find('.init-crop-button').removeAttr('disabled'); 197 | $field.find('.acf-image-value').data('original-image', originalImage); 198 | $field.find('.acf-image-value').data('cropped-image', originalImage); 199 | $field.find('.acf-image-value').data('cropped', false); 200 | $.post(ajaxurl, {action: 'acf_image_crop_get_image_size', image_id: originalImage}, function(data, textStatus, xhr) { 201 | if($field.find('img.crop-image').length == 0){ 202 | $field.find('.crop-action').append($('')); 203 | } 204 | $field.find('img.crop-image').attr('src', data['url']); 205 | $field.find('img.crop-image').data('width', data['width']); 206 | $field.find('img.crop-image').data('height', data['height']); 207 | var warnings = []; 208 | var valid = true; 209 | if($options.data('width') && data['width'] < $options.data('width')){ 210 | // changed for translation 211 | warnings.push( acf._e('image_crop', 'width_should_be') + $options.data('width') + 'px\n' + acf._e('image_crop', 'selected_width') + data['width'] + 'px'); 212 | // changed END 213 | valid = false; 214 | } 215 | if($options.data('height') && data['height'] < $options.data('height')){ 216 | // changed for translation 217 | warnings.push(acf._e('image_crop', 'height_should_be') + $options.data('height') + 'px\n' + acf._e('image_crop', 'selected_height') + data['height'] + 'px'); 218 | // changed END 219 | valid = false; 220 | } 221 | if(!valid){ 222 | $field.addClass('invalid'); 223 | $field.find('.init-crop-button').attr('disabled', 'disabled'); 224 | // changed for translation 225 | alert(acf._e('image_crop', 'size_warning') + '\n\n' + warnings.join('\n\n')); 226 | // changed END 227 | } 228 | else{ 229 | if($options.data('force_crop')){ 230 | initCrop($field); 231 | } 232 | } 233 | 234 | }, 'json'); 235 | updateFieldValue($field); 236 | } 237 | else{ 238 | //Do nothing 239 | } 240 | 241 | }); 242 | $field.find('.init-crop-button').click(function(e){ 243 | e.preventDefault(); 244 | initCrop($field); 245 | }); 246 | $field.find('.perform-crop-button').click(function(e){ 247 | e.preventDefault(); 248 | performCrop($field); 249 | }); 250 | $field.find('.cancel-crop-button').click(function(e){ 251 | e.preventDefault(); 252 | cancelCrop($field); 253 | }); 254 | $field.find('[data-name=edit]').click(function(e){ 255 | e.preventDefault(); 256 | e.stopPropagation(); 257 | var id = $field.find('.acf-image-value').data('cropped-image'); 258 | if(!$.isNumeric(id)){ 259 | id = $field.find('.acf-image-value').data('original-image');; 260 | } 261 | acf.media.popup({ 262 | mode : 'edit', 263 | title : acf._e('image', 'edit'), 264 | button : acf._e('image', 'update'), 265 | id : id 266 | }); 267 | }); 268 | 269 | } 270 | 271 | function initCrop($field){ 272 | var $options = $field.find('.acf-image-uploader'); 273 | var options = { 274 | handles: true, 275 | onSelectEnd: function (img, selection) { 276 | updateThumbnail($field, img, selection); 277 | updateCropData($field, img, selection); 278 | }, 279 | imageWidth:$options.find('.crop-stage img.crop-image').data('width'), 280 | imageHeight:$options.find('.crop-stage img.crop-image').data('height'), 281 | x1: 0, 282 | y1: 0 283 | }; 284 | if($options.data('crop_type') == 'hard'){ 285 | options.aspectRatio = $options.data('width') + ':' + $options.data('height'); 286 | options.minWidth = $options.data('width'); 287 | options.minHeight = $options.data('height'); 288 | options.x2 = $options.data('width'); 289 | options.y2 = $options.data('height'); 290 | } 291 | else if($options.data('crop_type') == 'min'){ 292 | if($options.data('width')){ 293 | options.minWidth = $options.data('width'); 294 | options.x2 = $options.data('width'); 295 | } 296 | else{ 297 | options.x2 = options.imageWidth; 298 | } 299 | if($options.data('height')){ 300 | options.minHeight = $options.data('height'); 301 | options.y2 = $options.data('height'); 302 | } 303 | else{ 304 | options.y2 = options.imageHeight; 305 | } 306 | } 307 | // Center crop - disabled needs more testing 308 | // options.x1 = options.imageWidth/2 - (options.minWidth/2); 309 | // options.y1 = options.imageHeight/2 - (options.minHeight/2) 310 | // options.x2 = options.minWidth + options.x1; 311 | // options.y2 = options.minHeight + options.y1; 312 | //options.y1 = (options.imageHeight - options.minHeight) / 2; 313 | if(!$field.hasClass('invalid')){ 314 | toggleCropView($field); 315 | $field.find('.crop-stage img.crop-image').imgAreaSelect(options); 316 | updateCropData($field, $field.find('.crop-stage img.crop-image').get(0), {y1: options.y1, y2: options.y2, x1: options.x1, x2: options.x2}); 317 | updateThumbnail($field, $field.find('.crop-stage img.crop-image').get(0), {y1: options.y1, y2: options.y2, x1: options.x1, x2: options.x2}); 318 | } 319 | } 320 | 321 | function updateCropData($field, img, selection){ 322 | var $options = $field.find('.acf-image-uploader'); 323 | $options.data('x1', selection.x1); 324 | $options.data('x2', selection.x2); 325 | $options.data('y1', selection.y1); 326 | $options.data('y2', selection.y2); 327 | } 328 | 329 | function updateThumbnail($field, img, selection){ 330 | console.log('updating thumbnail'); 331 | var $options = $field.find('.acf-image-uploader'); 332 | var div = $field.find('.crop-preview .preview'); 333 | var targetWidth = $field.find('.crop-preview .preview').width(); 334 | var factor = targetWidth / (selection.x2 - selection.x1); 335 | //image 336 | div.css('background-image', 'url(' + img.src + ')'); 337 | //width 338 | div.css('width', (selection.x2 - selection.x1) * factor); 339 | //height 340 | div.css('height', (selection.y2 - selection.y1) * factor); 341 | 342 | // Set offset - Fix by @christdg 343 | pos_x = 0-(selection.x1 * factor); 344 | pos_y = 0-(selection.y1 * factor); 345 | div.css('background-position', pos_x + 'px ' + pos_y + 'px'); 346 | 347 | div.css('background-size', $options.find('.crop-stage img.crop-image').data('width') * factor + 'px' + ' ' + $options.find('.crop-stage img.crop-image').data('height') * factor + 'px'); 348 | } 349 | 350 | function generateCropJSON(originalImage, croppedImage){ 351 | var obj = { 352 | original_image: originalImage, 353 | cropped_image: croppedImage 354 | } 355 | return JSON.stringify(obj); 356 | } 357 | 358 | function performCrop($field){ 359 | if(!$field.find('.crop-stage').hasClass('loading')){ 360 | $field.find('.crop-stage').addClass('loading'); 361 | var $options = $field.find('.acf-image-uploader'); 362 | var targetWidth = $options.data('width'); 363 | var targetHeight = $options.data('height'); 364 | var saveToMediaLibrary = $options.data('save_to_media_library'); 365 | if($options.data('crop_type') == 'min'){ 366 | targetWidth = $options.data('x2') - $options.data('x1'); 367 | targetHeight = $options.data('y2') - $options.data('y1'); 368 | } 369 | var data = { 370 | action: 'acf_image_crop_perform_crop', 371 | id: $field.find('.acf-image-value').data('original-image'), 372 | x1: $options.data('x1'), 373 | x2: $options.data('x2'), 374 | y1: $options.data('y1'), 375 | y2: $options.data('y2'), 376 | target_width: targetWidth, 377 | target_height: targetHeight, 378 | preview_size: $options.data('preview_size'), 379 | save_to_media_library: saveToMediaLibrary 380 | } 381 | $.post(ajaxurl, data, function(data, textStatus, xhr) { 382 | if(data.success){ 383 | $field.find('[data-name=image]').attr('src', data.preview_url); 384 | $field.find('.acf-image-value').data('cropped-image', data.value); 385 | $field.find('.acf-image-value').data('cropped', true); 386 | updateFieldValue($field); 387 | } 388 | else{ 389 | $field.append('

' + acf._e('image_crop', 'crop_error') + '

' + data.error_message); 390 | } 391 | $field.find('.crop-stage').removeClass('loading'); 392 | cancelCrop($field); 393 | }, 'json'); 394 | } 395 | } 396 | 397 | function cancelCrop($field){ 398 | toggleCropView($field); 399 | $field.find('.crop-stage img.crop-image').imgAreaSelect({remove:true}); 400 | } 401 | 402 | function toggleCropView($field){ 403 | var $innerField = $field.find('.acf-image-crop'); 404 | if($innerField.hasClass('cropping')){ 405 | $('#acf-image-crop-overlay').remove(); 406 | } 407 | else{ 408 | $('body').append($('
')); 409 | } 410 | $innerField.toggleClass('cropping'); 411 | 412 | } 413 | 414 | function updateFieldValue($field){ 415 | var $input = $field.find('.acf-image-value'); 416 | $input.val(generateCropJSON($input.data('original-image'), $input.data('cropped-image'))); 417 | } 418 | 419 | function getFullImageUrl(id, callback){ 420 | $.post(ajaxurl, {images: []}, function(data, textStatus, xhr) { 421 | }, 'json'); 422 | } 423 | 424 | 425 | if( typeof acf.add_action !== 'undefined' ) { 426 | 427 | /* 428 | * ready append (ACF5) 429 | * 430 | * These are 2 events which are fired during the page load 431 | * ready = on page load similar to $(document).ready() 432 | * append = on new DOM elements appended via repeater field 433 | * 434 | * @type event 435 | * @date 20/07/13 436 | * 437 | * @param $el (jQuery selection) the jQuery element which contains the ACF fields 438 | * @return n/a 439 | */ 440 | 441 | acf.add_action('ready append', function( $el ){ 442 | 443 | // search $el for fields of type 'image_crop' 444 | acf.get_fields({ type : 'image_crop'}, $el).each(function(){ 445 | 446 | initialize_field( $(this) ); 447 | 448 | }); 449 | 450 | }); 451 | 452 | 453 | } else { 454 | 455 | 456 | /* 457 | * acf/setup_fields (ACF4) 458 | * 459 | * This event is triggered when ACF adds any new elements to the DOM. 460 | * 461 | * @type function 462 | * @since 1.0.0 463 | * @date 01/01/12 464 | * 465 | * @param event e: an event object. This can be ignored 466 | * @param Element postbox: An element which contains the new HTML 467 | * 468 | * @return n/a 469 | */ 470 | 471 | $(document).live('acf/setup_fields', function(e, postbox){ 472 | 473 | $(postbox).find('.field[data-field_type="image_crop"]').each(function(){ 474 | 475 | initialize_field( $(this) ); 476 | 477 | }); 478 | 479 | }); 480 | 481 | 482 | } 483 | 484 | 485 | })(jQuery); 486 | -------------------------------------------------------------------------------- /js/input.js: -------------------------------------------------------------------------------- 1 | (function($){ 2 | acf.fields.image_crop = acf.field.extend({ 3 | 4 | type: 'image_crop', 5 | $el: null, 6 | 7 | actions: { 8 | 'ready': 'initialize', 9 | 'append': 'initialize' 10 | }, 11 | 12 | events: { 13 | 'click a[data-name="add"]': 'add', 14 | 'click a[data-name="edit"]': 'edit', 15 | 'click a[data-name="remove"]': 'remove', 16 | 'change input[type="file"]': 'change' 17 | }, 18 | 19 | focus: function(){ 20 | 21 | // get elements 22 | this.$el = this.$field.find('.acf-image-uploader'); 23 | 24 | // get options 25 | this.o = acf.get_data( this.$el ); 26 | 27 | 28 | }, 29 | 30 | initialize: function(){ 31 | // add attribute to form 32 | if( this.o.uploader == 'basic' ) { 33 | 34 | this.$el.closest('form').attr('enctype', 'multipart/form-data'); 35 | 36 | } 37 | 38 | }, 39 | 40 | add: function() { 41 | // reference 42 | var self = this, 43 | $field = this.$field; 44 | 45 | 46 | // get repeater 47 | var $repeater = acf.get_closest_field( this.$field, 'repeater' ); 48 | 49 | 50 | // popup 51 | var frame = acf.media.popup({ 52 | 53 | title: acf._e('image', 'select'), 54 | mode: 'select', 55 | type: 'image', 56 | field: acf.get_field_key($field), 57 | multiple: $repeater.exists(), 58 | library: this.o.library, 59 | mime_types: this.o.mime_types, 60 | 61 | select: function( attachment, i ) { 62 | 63 | // select / add another image field? 64 | if( i > 0 ) { 65 | 66 | // vars 67 | var key = acf.get_field_key( $field ), 68 | $tr = $field.closest('.acf-row'); 69 | 70 | 71 | // reset field 72 | $field = false; 73 | 74 | 75 | // find next image field 76 | $tr.nextAll('.acf-row:visible').each(function(){ 77 | 78 | // get next $field 79 | $field = acf.get_field( key, $(this) ); 80 | 81 | 82 | // bail early if $next was not found 83 | if( !$field ) { 84 | 85 | return; 86 | 87 | } 88 | 89 | 90 | // bail early if next file uploader has value 91 | if( $field.find('.acf-image-uploader.has-value').exists() ) { 92 | 93 | $field = false; 94 | return; 95 | 96 | } 97 | 98 | 99 | // end loop if $next is found 100 | return false; 101 | 102 | }); 103 | 104 | 105 | // add extra row if next is not found 106 | if( !$field ) { 107 | 108 | $tr = acf.fields.repeater.doFocus( $repeater ).add(); 109 | 110 | 111 | // bail early if no $tr (maximum rows hit) 112 | if( !$tr ) { 113 | 114 | return false; 115 | 116 | } 117 | 118 | 119 | // get next $field 120 | $field = acf.get_field( key, $tr ); 121 | 122 | } 123 | 124 | } 125 | 126 | // focus 127 | self.doFocus( $field ); 128 | 129 | 130 | // render 131 | self.render( self.prepare(attachment) ); 132 | 133 | } 134 | 135 | }); 136 | 137 | }, 138 | 139 | prepare: function( attachment ) { 140 | // vars 141 | var image = { 142 | id: attachment.id, 143 | url: attachment.attributes.url 144 | }; 145 | 146 | 147 | // check for preview size 148 | if( acf.isset(attachment.attributes, 'sizes', this.o.preview_size, 'url') ) { 149 | 150 | image.url = attachment.attributes.sizes[ this.o.preview_size ].url; 151 | 152 | } 153 | 154 | 155 | // return 156 | return image; 157 | 158 | }, 159 | 160 | render: function( image ){ 161 | 162 | 163 | // set atts 164 | this.$el.find('[data-name="image"]').attr( 'src', image.url ); 165 | this.$el.find('[data-name="id"]').val( image.id ).trigger('change'); 166 | 167 | 168 | // set div class 169 | this.$el.addClass('has-value'); 170 | 171 | }, 172 | 173 | edit: function() { 174 | // reference 175 | var self = this; 176 | 177 | 178 | // vars 179 | //var id = this.$el.find('[data-name="id"]').val(); 180 | 181 | var id = this.$el.find('.acf-image-value').data('cropped-image'); 182 | if(!$.isNumeric(id)){ 183 | id = this.$el.find('.acf-image-value').data('original-image');; 184 | } 185 | 186 | // popup 187 | var frame = acf.media.popup({ 188 | 189 | title: acf._e('image', 'edit'), 190 | type: 'image', 191 | button: acf._e('image', 'update'), 192 | mode: 'edit', 193 | id: id, 194 | 195 | select: function( attachment, i ) { 196 | 197 | self.render( self.prepare(attachment) ); 198 | 199 | } 200 | 201 | }); 202 | 203 | }, 204 | 205 | remove: function() { 206 | 207 | // vars 208 | var attachment = { 209 | id: '', 210 | url: '' 211 | }; 212 | 213 | 214 | // add file to field 215 | this.render( attachment ); 216 | 217 | 218 | // remove class 219 | this.$el.removeClass('has-value'); 220 | 221 | }, 222 | 223 | change: function( e ){ 224 | 225 | this.$el.find('[data-name="id"]').val( e.$el.val() ); 226 | 227 | } 228 | 229 | }); 230 | 231 | function initialize_field( $el ) { 232 | var $field = $el, $options = $el.find('.acf-image-uploader'); 233 | $field.find('.acf-image-value').on('change', function(){ 234 | var originalImage = $(this).val(); 235 | if($(this).val()){ 236 | $field.removeClass('invalid'); 237 | $field.find('.init-crop-button').removeAttr('disabled'); 238 | $field.find('.acf-image-value').data('original-image', originalImage); 239 | $field.find('.acf-image-value').data('cropped-image', originalImage); 240 | $field.find('.acf-image-value').data('cropped', false); 241 | $.post(ajaxurl, {action: 'acf_image_crop_get_image_size', image_id: originalImage}, function(data, textStatus, xhr) { 242 | if($field.find('img.crop-image').length == 0){ 243 | $field.find('.crop-action').append($('')); 244 | } 245 | $field.find('img.crop-image').attr('src', data['url']); 246 | $field.find('img.crop-image').data('width', data['width']); 247 | $field.find('img.crop-image').data('height', data['height']); 248 | var warnings = []; 249 | var valid = true; 250 | if($options.data('width') && data['width'] < $options.data('width')){ 251 | // changed for translation 252 | warnings.push( acf._e('image_crop', 'width_should_be') + $options.data('width') + 'px\n' + acf._e('image_crop', 'selected_width') + data['width'] + 'px'); 253 | // changed END 254 | valid = false; 255 | } 256 | if($options.data('height') && data['height'] < $options.data('height')){ 257 | // changed for translation 258 | warnings.push(acf._e('image_crop', 'height_should_be') + $options.data('height') + 'px\n' + acf._e('image_crop', 'selected_height') + data['height'] + 'px'); 259 | // changed END 260 | valid = false; 261 | } 262 | if(!valid){ 263 | $field.addClass('invalid'); 264 | $field.find('.init-crop-button').attr('disabled', 'disabled'); 265 | // changed for translation 266 | alert(acf._e('image_crop', 'size_warning') + '\n\n' + warnings.join('\n\n')); 267 | // changed END 268 | } 269 | else{ 270 | if($options.data('force_crop')){ 271 | initCrop($field); 272 | } 273 | } 274 | 275 | }, 'json'); 276 | updateFieldValue($field); 277 | } 278 | else{ 279 | //Do nothing 280 | } 281 | 282 | }); 283 | $field.find('.init-crop-button').click(function(e){ 284 | e.preventDefault(); 285 | initCrop($field); 286 | }); 287 | $field.find('.perform-crop-button').click(function(e){ 288 | e.preventDefault(); 289 | performCrop($field); 290 | }); 291 | $field.find('.cancel-crop-button').click(function(e){ 292 | e.preventDefault(); 293 | cancelCrop($field); 294 | }); 295 | // $field.find('[data-name=edit]').click(function(e){ 296 | // e.preventDefault(); 297 | // e.stopPropagation(); 298 | // var id = $field.find('.acf-image-value').data('cropped-image'); 299 | // if(!$.isNumeric(id)){ 300 | // id = $field.find('.acf-image-value').data('original-image');; 301 | // } 302 | // acf.media.popup({ 303 | // mode : 'edit', 304 | // title : acf._e('image', 'edit'), 305 | // button : acf._e('image', 'update'), 306 | // id : id 307 | // }); 308 | // }); 309 | 310 | } 311 | 312 | function initCrop($field){ 313 | var $options = $field.find('.acf-image-uploader'); 314 | var options = { 315 | handles: true, 316 | onSelectEnd: function (img, selection) { 317 | updateThumbnail($field, img, selection); 318 | updateCropData($field, img, selection); 319 | }, 320 | imageWidth:$options.find('.crop-stage img.crop-image').data('width'), 321 | imageHeight:$options.find('.crop-stage img.crop-image').data('height'), 322 | x1: 0, 323 | y1: 0 324 | }; 325 | if($options.data('crop_type') == 'hard'){ 326 | options.aspectRatio = $options.data('width') + ':' + $options.data('height'); 327 | options.minWidth = $options.data('width'); 328 | options.minHeight = $options.data('height'); 329 | options.x2 = $options.data('width'); 330 | options.y2 = $options.data('height'); 331 | } 332 | else if($options.data('crop_type') == 'min'){ 333 | if($options.data('width')){ 334 | options.minWidth = $options.data('width'); 335 | options.x2 = $options.data('width'); 336 | } 337 | else{ 338 | options.x2 = options.imageWidth; 339 | } 340 | if($options.data('height')){ 341 | options.minHeight = $options.data('height'); 342 | options.y2 = $options.data('height'); 343 | } 344 | else{ 345 | options.y2 = options.imageHeight; 346 | } 347 | } 348 | // Center crop - disabled needs more testing 349 | // options.x1 = options.imageWidth/2 - (options.minWidth/2); 350 | // options.y1 = options.imageHeight/2 - (options.minHeight/2) 351 | // options.x2 = options.minWidth + options.x1; 352 | // options.y2 = options.minHeight + options.y1; 353 | //options.y1 = (options.imageHeight - options.minHeight) / 2; 354 | if(!$field.hasClass('invalid')){ 355 | toggleCropView($field); 356 | $field.find('.crop-stage img.crop-image').imgAreaSelect(options); 357 | updateCropData($field, $field.find('.crop-stage img.crop-image').get(0), {y1: options.y1, y2: options.y2, x1: options.x1, x2: options.x2}); 358 | updateThumbnail($field, $field.find('.crop-stage img.crop-image').get(0), {y1: options.y1, y2: options.y2, x1: options.x1, x2: options.x2}); 359 | } 360 | } 361 | 362 | function updateCropData($field, img, selection){ 363 | var $options = $field.find('.acf-image-uploader'); 364 | $options.data('x1', selection.x1); 365 | $options.data('x2', selection.x2); 366 | $options.data('y1', selection.y1); 367 | $options.data('y2', selection.y2); 368 | } 369 | 370 | function updateThumbnail($field, img, selection){ 371 | var $options = $field.find('.acf-image-uploader'); 372 | var div = $field.find('.crop-preview .preview'); 373 | var targetWidth = $field.find('.crop-preview .preview').width(); 374 | var factor = targetWidth / (selection.x2 - selection.x1); 375 | //image 376 | div.css('background-image', 'url(' + img.src + ')'); 377 | //width 378 | div.css('width', (selection.x2 - selection.x1) * factor); 379 | //height 380 | div.css('height', (selection.y2 - selection.y1) * factor); 381 | 382 | // Set offset - Fix by @christdg 383 | pos_x = 0-(selection.x1 * factor); 384 | pos_y = 0-(selection.y1 * factor); 385 | div.css('background-position', pos_x + 'px ' + pos_y + 'px'); 386 | 387 | div.css('background-size', $options.find('.crop-stage img.crop-image').data('width') * factor + 'px' + ' ' + $options.find('.crop-stage img.crop-image').data('height') * factor + 'px'); 388 | } 389 | 390 | function generateCropJSON(originalImage, croppedImage){ 391 | var obj = { 392 | original_image: originalImage, 393 | cropped_image: croppedImage 394 | } 395 | return JSON.stringify(obj); 396 | } 397 | 398 | function performCrop($field){ 399 | if(!$field.find('.crop-stage').hasClass('loading')){ 400 | $field.find('.crop-stage').addClass('loading'); 401 | var $options = $field.find('.acf-image-uploader'); 402 | var targetWidth = $options.data('width'); 403 | var targetHeight = $options.data('height'); 404 | var saveToMediaLibrary = $options.data('save_to_media_library'); 405 | if($options.data('crop_type') == 'min'){ 406 | targetWidth = $options.data('x2') - $options.data('x1'); 407 | targetHeight = $options.data('y2') - $options.data('y1'); 408 | } 409 | var data = { 410 | action: 'acf_image_crop_perform_crop', 411 | id: $field.find('.acf-image-value').data('original-image'), 412 | x1: $options.data('x1'), 413 | x2: $options.data('x2'), 414 | y1: $options.data('y1'), 415 | y2: $options.data('y2'), 416 | target_width: targetWidth, 417 | target_height: targetHeight, 418 | preview_size: $options.data('preview_size'), 419 | save_to_media_library: saveToMediaLibrary 420 | } 421 | $.post(ajaxurl, data, function(data, textStatus, xhr) { 422 | if(data.success){ 423 | $field.find('[data-name=image]').attr('src', data.preview_url); 424 | $field.find('.acf-image-value').data('cropped-image', data.value); 425 | $field.find('.acf-image-value').data('cropped', true); 426 | updateFieldValue($field); 427 | } 428 | else{ 429 | $field.append('

' + acf._e('image_crop', 'crop_error') + '

' + data.error_message); 430 | } 431 | $field.find('.crop-stage').removeClass('loading'); 432 | cancelCrop($field); 433 | }, 'json'); 434 | } 435 | } 436 | 437 | function cancelCrop($field){ 438 | toggleCropView($field); 439 | $field.find('.crop-stage img.crop-image').imgAreaSelect({remove:true}); 440 | } 441 | 442 | function toggleCropView($field){ 443 | var $innerField = $field.find('.acf-image-crop'); 444 | if($innerField.hasClass('cropping')){ 445 | $('#acf-image-crop-overlay').remove(); 446 | } 447 | else{ 448 | $('body').append($('
')); 449 | } 450 | $innerField.toggleClass('cropping'); 451 | 452 | } 453 | 454 | function updateFieldValue($field){ 455 | var $input = $field.find('.acf-image-value'); 456 | $input.val(generateCropJSON($input.data('original-image'), $input.data('cropped-image'))); 457 | } 458 | 459 | function getFullImageUrl(id, callback){ 460 | $.post(ajaxurl, {images: []}, function(data, textStatus, xhr) { 461 | }, 'json'); 462 | } 463 | 464 | 465 | if( typeof acf.add_action !== 'undefined' ) { 466 | 467 | /* 468 | * ready append (ACF5) 469 | * 470 | * These are 2 events which are fired during the page load 471 | * ready = on page load similar to $(document).ready() 472 | * append = on new DOM elements appended via repeater field 473 | * 474 | * @type event 475 | * @date 20/07/13 476 | * 477 | * @param $el (jQuery selection) the jQuery element which contains the ACF fields 478 | * @return n/a 479 | */ 480 | 481 | acf.add_action('ready append', function( $el ){ 482 | 483 | // search $el for fields of type 'image_crop' 484 | acf.get_fields({ type : 'image_crop'}, $el).each(function(){ 485 | 486 | initialize_field( $(this) ); 487 | 488 | }); 489 | 490 | }); 491 | 492 | 493 | } else { 494 | 495 | 496 | /* 497 | * acf/setup_fields (ACF4) 498 | * 499 | * This event is triggered when ACF adds any new elements to the DOM. 500 | * 501 | * @type function 502 | * @since 1.0.0 503 | * @date 01/01/12 504 | * 505 | * @param event e: an event object. This can be ignored 506 | * @param Element postbox: An element which contains the new HTML 507 | * 508 | * @return n/a 509 | */ 510 | 511 | $(document).live('acf/setup_fields', function(e, postbox){ 512 | 513 | $(postbox).find('.field[data-field_type="image_crop"]').each(function(){ 514 | 515 | initialize_field( $(this) ); 516 | 517 | }); 518 | 519 | }); 520 | 521 | 522 | } 523 | 524 | 525 | })(jQuery); 526 | -------------------------------------------------------------------------------- /acf-image-crop-v4.php: -------------------------------------------------------------------------------- 1 | name = 'image_crop'; 23 | $this->label = __('Image with user-crop'); 24 | $this->category = __("Content",'acf'); // Basic, Content, Choice, etc 25 | $this->defaults = array( 26 | 'force_crop' => 'no', 27 | 'crop_type' => 'hard', 28 | 'preview_size' => 'medium', 29 | 'save_format' => 'id', 30 | 'save_in_media_library' => 'yes', 31 | 'target_size' => 'thumbnail', 32 | 'retina_mode' => 'no' 33 | // add default here to merge into your field. 34 | // This makes life easy when creating the field options as you don't need to use any if( isset('') ) logic. eg: 35 | //'preview_size' => 'thumbnail' 36 | ); 37 | 38 | $this->options = get_option( 'acf_image_crop_settings' ); 39 | 40 | //Call grandparent cunstructor 41 | acf_field::__construct(); 42 | 43 | // settings 44 | $this->settings = array( 45 | 'path' => apply_filters('acf/helpers/get_path', __FILE__), 46 | 'dir' => apply_filters('acf/helpers/get_dir', __FILE__), 47 | 'version' => '1.0.0' 48 | ); 49 | 50 | // add ajax action to be able to retrieve full image size via javascript 51 | add_action( 'wp_ajax_acf_image_crop_get_image_size', array( &$this, 'crop_get_image_size' ) ); 52 | add_action( 'wp_ajax_acf_image_crop_perform_crop', array( &$this, 'perform_crop' ) ); 53 | 54 | // add filter to media query function to hide cropped images from media library 55 | add_filter('ajax_query_attachments_args', array($this, 'filterMediaQuery')); 56 | 57 | // Register extra fields on the media settings page on admin_init 58 | add_action('admin_init', array($this, 'registerSettings')); 59 | 60 | } 61 | 62 | // AJAX handler for retieving full image dimensions from ID 63 | public function crop_get_image_size() 64 | { 65 | $img = wp_get_attachment_image_src( $_POST['image_id'], 'full'); 66 | if($img){ 67 | echo json_encode( array( 68 | 'url' => $img[0], 69 | 'width' => $img[1], 70 | 'height' => $img[2] 71 | ) ); 72 | } 73 | exit; 74 | } 75 | 76 | 77 | /* 78 | * create_options() 79 | * 80 | * Create extra options for your field. This is rendered when editing a field. 81 | * The value of $field['name'] can be used (like bellow) to save extra data to the $field 82 | * 83 | * @type action 84 | * @since 3.6 85 | * @date 23/01/13 86 | * 87 | * @param $field - an array holding all the field's data 88 | */ 89 | 90 | function create_options($field) 91 | { 92 | // defaults? 93 | /* 94 | $field = array_merge($this->defaults, $field); 95 | */ 96 | 97 | // key is needed in the field names to correctly save the data 98 | $key = $field['name']; 99 | 100 | 101 | // Create Field Options HTML 102 | ?> 103 | 104 | 105 | 106 |

107 | 108 | 109 | 'select', 113 | 'name' => 'fields[' . $key . '][crop_type]', 114 | 'value' => $field['crop_type'], 115 | 'class' => 'crop-type-select', 116 | 'choices' => array( 117 | 'hard' => __('Hard crop', 'acf'), 118 | 'min' => __('Minimal dimensions', 'acf') 119 | ) 120 | )); 121 | 122 | ?> 123 | 124 | 125 | 126 | 127 | 128 |

129 | 130 | 131 | __("Custom size",'acf'))); 133 | unset($sizes['full']); 134 | do_action('acf/create_field', array( 135 | 'type' => 'select', 136 | 'name' => 'fields[' . $key . '][target_size]', 137 | 'value' => $field['target_size'], 138 | 'class' => 'target-size-select', 139 | 'choices' => $sizes 140 | )); 141 | 142 | ?> 143 | 144 | 145 | 146 | 147 | 148 |

149 | 150 | 151 | 'text', 155 | 'name' => 'fields[' . $key . '][width]', 156 | 'value' => isset($field['width']) ? $field['width'] : '', 157 | 'class' => 'width dimension', 158 | 'placeholder' => 'Width' 159 | )); 160 | ?> 161 |
 : 
162 | 'text', 165 | 'name' => 'fields[' . $key . '][height]', 166 | 'value' => isset($field['height']) ? $field['height'] : '', 167 | 'class' => 'height dimension', 168 | 'placeholder' => 'Height' 169 | )); 170 | ?> 171 | 172 | 173 | 174 | 175 | 176 |

177 | 178 | 179 | 'select', 182 | 'name' => 'fields[' . $key . '][preview_size]', 183 | 'value' => $field['preview_size'], 184 | 'class' => 'preview-size-select', 185 | 'choices' => apply_filters('acf/get_image_sizes', array()) 186 | )); 187 | 188 | ?> 189 | 190 | 191 | 192 | 193 | 194 |

195 | 196 | 197 | 'radio', 200 | 'name' => 'fields['.$key.'][force_crop]', 201 | 'value' => $field['force_crop'], 202 | 'layout' => 'horizontal', 203 | 'choices' => array( 204 | 'yes' => __("Yes",'acf'), 205 | 'no' => __("No",'acf') 206 | ) 207 | )); 208 | ?> 209 | 210 | 211 | 212 | 213 | 214 |

215 | 216 | 217 | 'radio', 220 | 'name' => 'fields['.$key.'][save_in_media_library]', 221 | 'value' => $field['save_in_media_library'], 222 | 'layout' => 'horizontal', 223 | 'choices' => array( 224 | 'yes' => __("Yes",'acf'), 225 | 'no' => __("No",'acf') 226 | ), 227 | 'class' => 'save-in-media-library-select' 228 | )); 229 | ?> 230 | 231 | 232 | getOption('retina_mode')){ 235 | $retina_instructions .= '
' . __('NB. You currently have enabled retina mode globally for all fields through settings, which will override this setting.','acf-image_crop'); 236 | } 237 | ?> 238 | 239 | 240 | 241 |

242 | 243 | 244 | 'radio', 247 | 'name' => 'fields['.$key.'][retina_mode]', 248 | 'value' => $field['retina_mode'], 249 | 'layout' => 'horizontal', 250 | 'choices' => array( 251 | 'yes' => __("Yes",'acf'), 252 | 'no' => __("No",'acf') 253 | ), 254 | )); 255 | ?> 256 | 257 | 258 | 259 | 260 | 261 |

262 | 263 | 264 | 'radio', 267 | 'name' => 'fields['.$key.'][save_format]', 268 | 'value' => $field['save_format'], 269 | 'layout' => 'horizontal', 270 | 'choices' => array( 271 | 'url' => __("Image URL",'acf'), 272 | 'id' => __("Image ID",'acf'), 273 | 'object' => __("Image Object",'acf') 274 | ), 275 | 'class' => 'return-value-select' 276 | )); 277 | ?> 278 | 279 | 280 | defaults, $field); 302 | */ 303 | // perhaps use $field['preview_size'] to alter the markup? 304 | $data = json_decode($field['value']); 305 | // create Field HTML 306 | // vars 307 | $o = array( 308 | 'class' => '', 309 | 'url' => '', 310 | ); 311 | // $originalImage = null; 312 | // if( $data && is_object($data) && is_numeric($data->original_image) ) 313 | // { 314 | // $originalImage = wp_get_attachment_image_src($data->original_image, 'full'); 315 | // $url = wp_get_attachment_image_src($data->original_image, $field['preview_size']); 316 | // if($field['save_in_media_library'] == 'yes'){ 317 | // if(is_numeric($data->cropped_image)){ 318 | // $url = wp_get_attachment_image_src($data->cropped_image, $field['preview_size']); 319 | // } 320 | // } 321 | // else{ 322 | // if($data->cropped_image_url){ 323 | // $url = $data->cropped_image_url; 324 | // } 325 | // else{ 326 | 327 | // } 328 | // } 329 | 330 | // $o['class'] = 'active'; 331 | // $o['url'] = $url[0]; 332 | // } 333 | $imageData = $this->get_image_data($field); 334 | //print_r($field); 335 | $originalImage = wp_get_attachment_image_src($imageData->original_image, 'full'); 336 | if($imageData->original_image){ 337 | $o['class'] = 'active'; 338 | $o['url'] = $imageData->preview_image_url; 339 | } 340 | $width = 0; 341 | $height = 0; 342 | if($field['target_size'] == 'custom'){ 343 | $width = $field['width']; 344 | $height = $field['height']; 345 | } 346 | else{ 347 | global $_wp_additional_image_sizes; 348 | $s = $field['target_size']; 349 | if (isset($_wp_additional_image_sizes[$s])) { 350 | $width = intval($_wp_additional_image_sizes[$s]['width']); 351 | $height = intval($_wp_additional_image_sizes[$s]['height']); 352 | } else { 353 | $width = get_option($s.'_size_w'); 354 | $height = get_option($s.'_size_h'); 355 | } 356 | } 357 | 358 | // Retina mode 359 | if($this->getOption('retina_mode') || $field['retina_mode'] == 'yes'){ 360 | $width = $width * 2; 361 | $height = $height * 2; 362 | } 363 | ?> 364 |
data-save-to-media-library="" > 365 | 366 |
367 |
368 |
369 |
    370 |
  • 371 |
  • 372 |
373 |
374 | 375 |
376 |
377 |
378 |
379 |

Crop the image

380 | original_image ): ?> 381 | 382 | 383 |
384 |
385 |

Preview

386 |
387 |
388 | Cancel Crop! 389 |
390 |
391 | 392 |
393 | Crop 394 |
395 |
396 |
397 |

398 |

399 |
400 | cropped_image; 431 | } 432 | else{ 433 | // We are migrating from a standard image field 434 | $data = new stdClass(); 435 | $data->cropped_image = $value; 436 | $data->original_image = $value; 437 | } 438 | 439 | // format 440 | if( $field['save_format'] == 'url' ) 441 | { 442 | if(is_numeric($data->cropped_image)){ 443 | $value = wp_get_attachment_url( $data->cropped_image ); 444 | } 445 | elseif(is_array($data->cropped_image)){ 446 | 447 | $value = $this->getAbsoluteImageUrl($data->cropped_image['image']); 448 | } 449 | elseif(is_object($data->cropped_image)){ 450 | $value = $this->getAbsoluteImageUrl($data->cropped_image->image); 451 | } 452 | 453 | } 454 | elseif( $field['save_format'] == 'object' ) 455 | { 456 | if(is_numeric($data->cropped_image )){ 457 | $value = $this->getImageArray($data->cropped_image); 458 | $value['original_image'] = $this->getImageArray($data->original_image); 459 | // $attachment = get_post( $data->cropped_image ); 460 | // // validate 461 | // if( !$attachment ) 462 | // { 463 | // return false; 464 | // } 465 | 466 | 467 | // // create array to hold value data 468 | // $src = wp_get_attachment_image_src( $attachment->ID, 'full' ); 469 | 470 | // $value = array( 471 | // 'id' => $attachment->ID, 472 | // 'alt' => get_post_meta($attachment->ID, '_wp_attachment_image_alt', true), 473 | // 'title' => $attachment->post_title, 474 | // 'caption' => $attachment->post_excerpt, 475 | // 'description' => $attachment->post_content, 476 | // 'mime_type' => $attachment->post_mime_type, 477 | // 'url' => $src[0], 478 | // 'width' => $src[1], 479 | // 'height' => $src[2], 480 | // 'sizes' => array(), 481 | // ); 482 | 483 | 484 | // // find all image sizes 485 | // $image_sizes = get_intermediate_image_sizes(); 486 | 487 | // if( $image_sizes ) 488 | // { 489 | // foreach( $image_sizes as $image_size ) 490 | // { 491 | // // find src 492 | // $src = wp_get_attachment_image_src( $attachment->ID, $image_size ); 493 | 494 | // // add src 495 | // $value[ 'sizes' ][ $image_size ] = $src[0]; 496 | // $value[ 'sizes' ][ $image_size . '-width' ] = $src[1]; 497 | // $value[ 'sizes' ][ $image_size . '-height' ] = $src[2]; 498 | // } 499 | // // foreach( $image_sizes as $image_size ) 500 | // } 501 | } 502 | elseif(is_array( $data->cropped_image) || is_object($data->cropped_image)){ 503 | // Cropped image is not saved to media directory. Get data from original image instead 504 | $value = $this->getImageArray($data->original_image); 505 | 506 | // Get the relative url from data 507 | $relativeUrl = ''; 508 | if(is_array( $data->cropped_image)){ 509 | $relativeUrl = $data->cropped_image['image']; 510 | } 511 | else{ 512 | $relativeUrl = $data->cropped_image->image; 513 | } 514 | 515 | // Replace URL with cropped version 516 | $value['url'] = $this->getAbsoluteImageUrl($relativeUrl); 517 | 518 | // Calculate and replace sizes 519 | $imagePath = $this->getImagePath($relativeUrl); 520 | $dimensions = getimagesize($imagePath); 521 | $value['width'] = $dimensions[0]; 522 | $value['height'] = $dimensions[1]; 523 | 524 | // Add original image info 525 | $value['original_image'] = $this->getImageArray($data->original_image); 526 | } 527 | else{ 528 | 529 | //echo 'ELSE'; 530 | } 531 | 532 | } 533 | return $value; 534 | 535 | } 536 | 537 | 538 | function getImageArray($id){ 539 | $attachment = get_post( $id ); 540 | // validate 541 | if( !$attachment ) 542 | { 543 | return false; 544 | } 545 | 546 | 547 | // create array to hold value data 548 | $src = wp_get_attachment_image_src( $attachment->ID, 'full' ); 549 | 550 | $imageArray = array( 551 | 'id' => $attachment->ID, 552 | 'alt' => get_post_meta($attachment->ID, '_wp_attachment_image_alt', true), 553 | 'title' => $attachment->post_title, 554 | 'caption' => $attachment->post_excerpt, 555 | 'description' => $attachment->post_content, 556 | 'mime_type' => $attachment->post_mime_type, 557 | 'url' => $src[0], 558 | 'width' => $src[1], 559 | 'height' => $src[2], 560 | 'sizes' => array(), 561 | ); 562 | 563 | 564 | // find all image sizes 565 | $image_sizes = get_intermediate_image_sizes(); 566 | 567 | if( $image_sizes ) 568 | { 569 | foreach( $image_sizes as $image_size ) 570 | { 571 | // find src 572 | $src = wp_get_attachment_image_src( $attachment->ID, $image_size ); 573 | 574 | // add src 575 | $imageArray[ 'sizes' ][ $image_size ] = $src[0]; 576 | $imageArray[ 'sizes' ][ $image_size . '-width' ] = $src[1]; 577 | $imageArray[ 'sizes' ][ $image_size . '-height' ] = $src[2]; 578 | } 579 | } 580 | return $imageArray; 581 | } 582 | 583 | /* 584 | * input_admin_enqueue_scripts() 585 | * 586 | * This action is called in the admin_enqueue_scripts action on the edit screen where your field is created. 587 | * Use this action to add css + javascript to assist your create_field() action. 588 | * 589 | * $info http://codex.wordpress.org/Plugin_API/Action_Reference/admin_enqueue_scripts 590 | * @type action 591 | * @since 3.6 592 | * @date 23/01/13 593 | */ 594 | 595 | function input_admin_enqueue_scripts() 596 | { 597 | // register acf scripts 598 | wp_register_script('acf-input-image_crop', plugins_url('js/input-v4.js', __FILE__), array('acf-input', 'imgareaselect'), $this->settings['version']); 599 | 600 | wp_register_style('acf-input-image_crop', plugins_url('css/input.css', __FILE__), array('acf-input'), $this->settings['version']); 601 | wp_register_script( 'jcrop', includes_url( 'js/jcrop/jquery.Jcrop.min.css' )); 602 | 603 | 604 | // scripts 605 | wp_enqueue_script(array( 606 | 'acf-input-image_crop' 607 | )); 608 | 609 | wp_localize_script( 'acf-input-image_crop', 'ajax', array('nonce' => wp_create_nonce('acf_nonce')) ); 610 | 611 | // styles 612 | wp_enqueue_style(array( 613 | 'acf-input-image_crop', 614 | 'imgareaselect' 615 | )); 616 | 617 | } 618 | 619 | 620 | /* 621 | * field_group_admin_enqueue_scripts() 622 | * 623 | * This action is called in the admin_enqueue_scripts action on the edit screen where your field is edited. 624 | * Use this action to add css + javascript to assist your create_field_options() action. 625 | * 626 | * $info http://codex.wordpress.org/Plugin_API/Action_Reference/admin_enqueue_scripts 627 | * @type action 628 | * @since 3.6 629 | * @date 23/01/13 630 | */ 631 | 632 | function field_group_admin_enqueue_scripts() 633 | { 634 | // Note: This function can be removed if not used 635 | wp_register_script('acf-input-image-crop-options', $this->settings['dir'] . 'js/options-v4.js', array('jquery'), $this->settings['version']); 636 | wp_enqueue_script( 'acf-input-image-crop-options'); 637 | 638 | wp_register_style('acf-input-image-crop-options', $this->settings['dir'] . 'css/options.css'); 639 | wp_enqueue_style( 'acf-input-image-crop-options'); 640 | } 641 | 642 | 643 | /* 644 | * update_value() 645 | * 646 | * This filter is appied to the $value before it is updated in the db 647 | * 648 | * @type filter 649 | * @since 3.6 650 | * @date 23/01/13 651 | * 652 | * @param $value - the value which will be saved in the database 653 | * @param $post_id - the $post_id of which the value will be saved 654 | * @param $field - the field array holding all the field options 655 | * 656 | * @return $value - the modified value 657 | */ 658 | 659 | function update_value( $value, $post_id, $field ) 660 | { 661 | // array? 662 | if( is_array($value) && isset($value['id']) ) 663 | { 664 | $value = $value['id']; 665 | } 666 | 667 | // object? 668 | if( is_object($value) && isset($value->ID) ) 669 | { 670 | $value = $value->ID; 671 | } 672 | 673 | return $value; 674 | } 675 | 676 | function get_image_data($field){ 677 | $imageData = new stdClass(); 678 | $imageData->original_image = ''; 679 | $imageData->original_image_width = ''; 680 | $imageData->original_image_height = ''; 681 | $imageData->cropped_image = ''; 682 | $imageData->original_image_url = ''; 683 | $imageData->preview_image_url = ''; 684 | $imageData->image_url = ''; 685 | 686 | if($field['value'] == ''){ 687 | // Field has not yet been saved or is an empty image field 688 | return $imageData; 689 | } 690 | 691 | $data = json_decode($field['value']); 692 | 693 | if(! is_object($data)){ 694 | // Field was saved as a regular image field 695 | $imageAtts = wp_get_attachment_image_src($field['value'], 'full'); 696 | $imageData->original_image = $field['value']; 697 | $imageData->original_image_width = $imageAtts[1]; 698 | $imageData->original_image_height = $imageAtts[2]; 699 | $imageData->preview_image_url = $this->get_image_src($field['value'], $field['preview_size']); 700 | $imageData->image_url = $this->get_image_src($field['value'], 'full'); 701 | $imageData->original_image_url = $imageData->image_url; 702 | return $imageData; 703 | } 704 | 705 | if( !is_numeric($data->original_image) ) 706 | { 707 | // The field has been saved, but has no image 708 | return $imageData; 709 | } 710 | 711 | // By now, we have at least a saved original image 712 | $imageAtts = wp_get_attachment_image_src($data->original_image, 'full'); 713 | $imageData->original_image = $data->original_image; 714 | $imageData->original_image_width = $imageAtts[1]; 715 | $imageData->original_image_height = $imageAtts[2]; 716 | $imageData->original_image_url = $this->get_image_src($data->original_image, 'full'); 717 | 718 | // Set defaults to original image 719 | $imageData->image_url = $this->get_image_src($data->original_image, 'full'); 720 | $imageData->preview_image_url = $this->get_image_src($data->original_image, $field['preview_size']); 721 | 722 | // Check if there is a cropped version and set appropriate attributes 723 | if(is_numeric($data->cropped_image)){ 724 | // Cropped image was saved to media library ans has an ID 725 | $imageData->cropped_image = $data->cropped_image; 726 | $imageData->image_url = $this->get_image_src($data->cropped_image, 'full'); 727 | $imageData->preview_image_url = $this->get_image_src($data->cropped_image, $field['preview_size']); 728 | } 729 | elseif(is_object($data->cropped_image)){ 730 | // Cropped image was not saved to media library and is only stored by its URL 731 | $imageData->cropped_image = $data->cropped_image; 732 | 733 | // Generate appropriate URLs 734 | $mediaDir = wp_upload_dir(); 735 | $imageData->image_url = $mediaDir['baseurl'] . '/' . $data->cropped_image->image; 736 | $imageData->preview_image_url = $mediaDir['baseurl'] . '/' . $data->cropped_image->preview; 737 | } 738 | return $imageData; 739 | } 740 | 741 | function perform_crop(){ 742 | $targetWidth = $_POST['target_width']; 743 | $targetHeight = $_POST['target_height']; 744 | $saveToMediaLibrary = $_POST['save_to_media_library'] == 'yes'; 745 | $imageData = $this->generate_cropped_image($_POST['id'], $_POST['x1'], $_POST['x2'], $_POST['y1'], $_POST['y2'], $targetWidth, $targetHeight, $saveToMediaLibrary, $_POST['preview_size']); 746 | // $previewUrl = wp_get_attachment_image_src( $id, $_POST['preview_size']); 747 | // $fullUrl = wp_get_attachment_image_src( $id, 'full'); 748 | echo json_encode($imageData); 749 | die(); 750 | } 751 | 752 | function generate_cropped_image($id, $x1, $x2, $y1, $y2, $targetW, $targetH, $saveToMediaLibrary, $previewSize){//$id, $x1, $x2, $y$, $y2, $targetW, $targetH){ 753 | require_once ABSPATH . "/wp-admin/includes/file.php"; 754 | require_once ABSPATH . "/wp-admin/includes/image.php"; 755 | 756 | // Create the variable that will hold the new image data 757 | $imageData = array(); 758 | 759 | // Fetch media library info 760 | $mediaDir = wp_upload_dir(); 761 | 762 | // Get original image info 763 | $originalImageData = wp_get_attachment_metadata($id); 764 | 765 | // Get image editor from original image path to crop the image 766 | $image = wp_get_image_editor( $mediaDir['basedir'] . '/' . $originalImageData['file'] ); 767 | 768 | if(! is_wp_error( $image ) ){ 769 | 770 | // Crop the image using the provided measurements 771 | $image->crop($x1, $y1, $x2 - $x1, $y2 - $y1, $targetW, $targetH); 772 | 773 | // Retrieve original filename and seperate it from its file extension 774 | $originalFileName = explode('.', basename($originalImageData['file'])); 775 | 776 | // Retrieve and remove file extension from array 777 | $originalFileExtension = array_pop($originalFileName); 778 | 779 | // Generate new base filename 780 | $targetFileName = implode('.', $originalFileName) . '_' . $targetW . 'x' . $targetH . apply_filters('acf-image-crop/filename_postfix', '_acf_cropped') . '.' . $originalFileExtension; 781 | 782 | // Generate target path new file using existing media library 783 | $targetFilePath = $mediaDir['path'] . '/' . wp_unique_filename( $mediaDir['path'], $targetFileName); 784 | 785 | // Get the relative path to save as the actual image url 786 | $targetRelativePath = str_replace($mediaDir['basedir'] . '/', '', $targetFilePath); 787 | 788 | // Save the image to the target path 789 | if(is_wp_error($image->save($targetFilePath))){ 790 | // There was an error saving the image 791 | //TODO handle it 792 | } 793 | 794 | // If file should be saved to media library, create an attachment for it at get the new attachment ID 795 | if($saveToMediaLibrary){ 796 | // Generate attachment from created file 797 | 798 | // Get the filetype 799 | $wp_filetype = wp_check_filetype(basename($targetFilePath), null ); 800 | $attachment = array( 801 | 'guid' => $mediaDir['url'] . '/' . basename($targetFilePath), 802 | 'post_mime_type' => $wp_filetype['type'], 803 | 'post_title' => preg_replace('/\.[^.]+$/', '', basename($targetFilePath)), 804 | 'post_content' => '', 805 | 'post_status' => 'inherit' 806 | ); 807 | $attachmentId = wp_insert_attachment( $attachment, $targetFilePath); 808 | $attachmentData = wp_generate_attachment_metadata( $attachmentId, $targetFilePath ); 809 | wp_update_attachment_metadata( $attachmentId, $attachmentData ); 810 | add_post_meta($attachmentId, 'acf_is_cropped', 'true', true); 811 | 812 | // Add the id to the imageData-array 813 | $imageData['value'] = $attachmentId; 814 | 815 | // Add the image url 816 | $imageUrlObject = wp_get_attachment_image_src( $attachmentId, 'full'); 817 | $imageData['url'] = $imageUrlObject[0]; 818 | 819 | // Add the preview url as well 820 | $previewUrlObject = wp_get_attachment_image_src( $attachmentId, $previewSize); 821 | $imageData['preview_url'] = $previewUrlObject[0]; 822 | } 823 | // Else we need to return the actual path of the cropped image 824 | else{ 825 | // Add the image url to the imageData-array 826 | $imageData['value'] = array('image' => $targetRelativePath); 827 | $imageData['url'] = $mediaDir['baseurl'] . '/' . $targetRelativePath; 828 | 829 | // Get preview size dimensions 830 | global $_wp_additional_image_sizes; 831 | $previewWidth = 0; 832 | $previewHeight = 0; 833 | $crop = 0; 834 | if (isset($_wp_additional_image_sizes[$previewSize])) { 835 | $previewWidth = intval($_wp_additional_image_sizes[$previewSize]['width']); 836 | $previewHeight = intval($_wp_additional_image_sizes[$previewSize]['height']); 837 | $crop = $_wp_additional_image_sizes[$previewSize]['crop']; 838 | } else { 839 | $previewWidth = get_option($previewSize.'_size_w'); 840 | $previewHeight = get_option($previewSize.'_size_h'); 841 | $crop = get_option($previewSize.'_crop'); 842 | } 843 | 844 | // Generate preview file path 845 | $previewFilePath = $mediaDir['path'] . '/' . wp_unique_filename( $mediaDir['path'], 'preview_' . $targetFileName); 846 | $previewRelativePath = str_replace($mediaDir['basedir'] . '/', '', $previewFilePath); 847 | 848 | // Get image editor from cropped image 849 | $croppedImage = wp_get_image_editor( $targetFilePath ); 850 | $croppedImage->resize($previewWidth, $previewHeight, $crop); 851 | 852 | // Save the preview 853 | $croppedImage->save($previewFilePath); 854 | 855 | // Add the preview url 856 | $imageData['preview_url'] = $mediaDir['baseurl'] . '/' . $previewRelativePath; 857 | $imageData['value']['preview'] = $previewRelativePath; 858 | } 859 | $imageData['success'] = true; 860 | return $imageData; 861 | } 862 | else{ 863 | // Handle WP_ERROR 864 | $response = array(); 865 | $response['success'] = false; 866 | $response['error_message'] = ''; 867 | foreach($image->error_data as $code => $message){ 868 | $response['error_message'] .= '

' . $code . ': ' . $message . '

'; 869 | } 870 | return $response; 871 | } 872 | } 873 | 874 | function get_image_src($id, $size = 'thumbnail'){ 875 | $atts = wp_get_attachment_image_src( $id, $size); 876 | return $atts[0]; 877 | } 878 | 879 | function getAbsoluteImageUrl($relativeUrl){ 880 | $mediaDir = wp_upload_dir(); 881 | return $mediaDir['baseurl'] . '/' . $relativeUrl; 882 | } 883 | 884 | function getImagePath($relativePath){ 885 | $mediaDir = wp_upload_dir(); 886 | return $mediaDir['basedir'] . '/' . $relativePath; 887 | } 888 | 889 | function filterMediaQuery($args){ 890 | // get options 891 | $options = get_option( 'acf_image_crop_settings' ); 892 | $hide = isset($options['hide_cropped']) && $options['hide_cropped']; 893 | 894 | // If hide option is enabled, do not select items with the acf_is_cropped meta-field 895 | if($hide){ 896 | $args['meta_query']= array( 897 | array( 898 | 'key' => 'acf_is_cropped', 899 | 'compare' => 'NOT EXISTS' 900 | ) 901 | ); 902 | } 903 | return $args; 904 | } 905 | 906 | 907 | function registerSettings(){ 908 | add_settings_section( 909 | 'acf_image_crop_settings', 910 | __('ACF Image Crop Settings','acf-image_crop'), 911 | array($this, 'displayImageCropSettingsSection'), 912 | 'media' 913 | ); 914 | 915 | register_setting( 916 | 'media', // settings page 917 | 'acf_image_crop_settings' // option name 918 | ); 919 | 920 | add_settings_field( 921 | 'acf_image_crop_hide_cropped', // id 922 | __('Hide cropped images from media dialog', 'acf-image_crop'), // setting title 923 | array($this, 'displayHideFromMediaInput'), // display callback 924 | 'media', // settings page 925 | 'acf_image_crop_settings' // settings section 926 | ); 927 | 928 | add_settings_field( 929 | 'acf_image_crop_retina_mode', // id 930 | __('Enable global retina mode (beta)', 'acf-image_crop'), // setting title 931 | array($this, 'displayRetinaModeInput'), // display callback 932 | 'media', // settings page 933 | 'acf_image_crop_settings' // settings section 934 | ); 935 | } 936 | 937 | function displayHideFromMediaInput(){ 938 | // Get plugin options 939 | $options = get_option( 'acf_image_crop_settings' ); 940 | $hide = isset($options['hide_cropped']) && $options['hide_cropped']; 941 | 942 | // echo the field 943 | ?> 944 | value='true' /> 946 | 956 | value='true' /> 958 | options[$key]) ? $this->options[$key] : null; 967 | } 968 | 969 | } 970 | 971 | 972 | // create field 973 | new acf_field_image_crop(); 974 | 975 | ?> 976 | -------------------------------------------------------------------------------- /acf-image-crop-v5.php: -------------------------------------------------------------------------------- 1 | name = 'image_crop'; 26 | 27 | 28 | /* 29 | * label (string) Multiple words, can include spaces, visible when selecting a field type 30 | */ 31 | 32 | $this->label = __('Image with user-crop', 'acf-image_crop'); 33 | 34 | 35 | /* 36 | * category (string) basic | content | choice | relational | jquery | layout | CUSTOM GROUP NAME 37 | */ 38 | 39 | $this->category = 'content'; 40 | 41 | 42 | /* 43 | * defaults (array) Array of default settings which are merged into the field object. These are used later in settings 44 | */ 45 | 46 | $this->defaults = array( 47 | 'force_crop' => 'no', 48 | 'crop_type' => 'hard', 49 | 'preview_size' => 'medium', 50 | 'save_format' => 'id', 51 | 'save_in_media_library' => 'yes', 52 | 'target_size' => 'thumbnail', 53 | 'library' => 'all', 54 | 'retina_mode' => 'no' 55 | ); 56 | 57 | $this->options = get_option( 'acf_image_crop_settings' ); 58 | 59 | // add ajax action to be able to retrieve full image size via javascript 60 | add_action( 'wp_ajax_acf_image_crop_get_image_size', array( &$this, 'crop_get_image_size' ) ); 61 | add_action( 'wp_ajax_acf_image_crop_perform_crop', array( &$this, 'perform_crop' ) ); 62 | 63 | 64 | // add filter to media query function to hide cropped images from media library 65 | add_filter('ajax_query_attachments_args', array($this, 'filterMediaQuery')); 66 | 67 | // Register extra fields on the media settings page on admin_init 68 | add_action('admin_init', array($this, 'registerSettings')); 69 | 70 | /* 71 | * l10n (array) Array of strings that are used in JavaScript. This allows JS strings to be translated in PHP and loaded via: 72 | * var message = acf._e('image_crop', 'error'); 73 | */ 74 | 75 | $this->l10n = array( 76 | 'width_should_be' => __( 'Width should be at least: ','acf-image_crop' ), 77 | 'height_should_be' => __( 'Height should be at least: ','acf-image_crop' ), 78 | 'selected_width' => __( 'Selected image width: ','acf-image_crop' ), 79 | 'selected_height' => __( 'Selected image height: ','acf-image_crop' ), 80 | 'size_warning' => __( 'Warning: The selected image is smaller than the required size!','acf-image_crop' ), 81 | 'crop_error' => __( 'Sorry, an error occurred when trying to crop your image:') 82 | ); 83 | } 84 | 85 | // AJAX handler for retieving full image dimensions from ID 86 | public function crop_get_image_size() 87 | { 88 | $img = wp_get_attachment_image_src( $_POST['image_id'], 'full'); 89 | if($img){ 90 | echo json_encode( array( 91 | 'url' => $img[0], 92 | 'width' => $img[1], 93 | 'height' => $img[2] 94 | ) ); 95 | } 96 | exit; 97 | } 98 | 99 | 100 | /* 101 | * render_field_settings() 102 | * 103 | * Create extra settings for your field. These are visible when editing a field 104 | * 105 | * @type action 106 | * @since 3.6 107 | * @date 23/01/13 108 | * 109 | * @param $field (array) the $field being edited 110 | * @return n/a 111 | */ 112 | 113 | function render_field_settings( $field ) { 114 | 115 | /* 116 | * acf_render_field_setting 117 | * 118 | * This function will create a setting for your field. Simply pass the $field parameter and an array of field settings. 119 | * The array of settings does not require a `value` or `prefix`; These settings are found from the $field array. 120 | * 121 | * More than one setting can be added by copy/paste the above code. 122 | * Please note that you must also have a matching $defaults value for the field name (font_size) 123 | */ 124 | 125 | // crop_type 126 | acf_render_field_setting( $field, array( 127 | 'label' => __('Crop type','acf-image_crop'), 128 | 'instructions' => __('Select the type of crop the user should perform','acf-image_crop'), 129 | 'type' => 'select', 130 | 'name' => 'crop_type', 131 | 'layout' => 'horizontal', 132 | 'class' => 'crop-type-select', 133 | 'choices' => array( 134 | 'hard' => __('Hard crop', 'acf-image_crop'), 135 | 'min' => __('Minimal dimensions', 'acf-image_crop') 136 | ) 137 | )); 138 | 139 | // target_size 140 | $sizes = acf_get_image_sizes(); 141 | // added 142 | $instructions = __('Select the target size for this field','acf-image_crop'); 143 | if ( $this->getOption('retina_mode') ) { 144 | $instructions .= '

'.__('Retina mode is enabled - user will be required to select an image twice this size','acf-image_crop').''; 145 | } 146 | // added END 147 | $sizes['custom'] = __('Custom size', 'acf-image_crop'); 148 | acf_render_field_setting( $field, array( 149 | 'label' => __('Target size','acf-image_crop'), 150 | 'instructions' => $instructions, 151 | 'type' => 'select', 152 | 'name' => 'target_size', 153 | 'class' => 'target-size-select', 154 | 'choices' => $sizes 155 | )); 156 | 157 | // width - conditional: target_size == 'custom' 158 | acf_render_field_setting( $field, array( 159 | 'label' => __('Custom target width','acf-image_crop'), 160 | 'instructions' => __('Leave blank for no restriction (does not work with hard crop option)','acf-image_crop'), 161 | 'type' => 'number', 162 | 'name' => 'width', 163 | 'class' => 'custom-target-width custom-target-dimension', 164 | 'append' => 'px' 165 | )); 166 | 167 | // height - conditional: target_size == 'custom' 168 | acf_render_field_setting( $field, array( 169 | 'label' => __('Custom target height','acf-image_crop'), 170 | 'instructions' => __('Leave blank for no restriction (does not work with hard crop option)','acf-image_crop'), 171 | 'type' => 'number', 172 | 'name' => 'height', 173 | 'class' => 'custom-target-height custom-target-dimension', 174 | 'append' => 'px' 175 | )); 176 | 177 | // preview_size 178 | acf_render_field_setting( $field, array( 179 | 'label' => __('Preview Size','acf'), 180 | 'instructions' => __('Shown when entering data','acf'), 181 | 'type' => 'select', 182 | 'name' => 'preview_size', 183 | 'choices' => acf_get_image_sizes() 184 | )); 185 | 186 | // force_crop 187 | acf_render_field_setting( $field, array( 188 | 'label' => __('Force crop','acf-image_crop'), 189 | 'instructions' => __('Force the user to crop the image as soon at it is selected','acf-image_crop'), 190 | 'type' => 'radio', 191 | 'layout' => 'horizontal', 192 | 'name' => 'force_crop', 193 | 'choices' => array('yes' => __('Yes', 'acf'), 'no' => __('No', 'acf')) 194 | )); 195 | 196 | // save_in_media_library 197 | acf_render_field_setting( $field, array( 198 | 'label' => __('Save cropped image to media library','acf-image_crop'), 199 | 'instructions' => __('If the cropped image is not saved in the media library, "Image URL" is the only available return value.','acf-image_crop'), 200 | 'type' => 'radio', 201 | 'layout' => 'horizontal', 202 | 'name' => 'save_in_media_library', 203 | 'class' => 'save-in-media-library-select', 204 | 'choices' => array('yes' => __('Yes', 'acf'), 'no' => __('No', 'acf')) 205 | )); 206 | 207 | // only show this setting if the global option for retina mode is not activated 208 | // retina mode 209 | if ( !$this->getOption('retina_mode') ) { 210 | acf_render_field_setting( $field, array( 211 | 'label' => __('Retina/@2x mode ','acf-image_crop'), 212 | 'instructions' => __('Require and crop double the size set for this image. Enable this if you are using plugins like WP Retina 2x.','acf-image_crop'), 213 | 'type' => 'radio', 214 | 'layout' => 'horizontal', 215 | 'name' => 'retina_mode', 216 | 'choices' => array('yes' => __('Yes', 'acf'), 'no' => __('No', 'acf')) 217 | )); 218 | } 219 | // return_format 220 | acf_render_field_setting( $field, array( 221 | 'label' => __('Return Value','acf'), 222 | 'instructions' => __('Specify the returned value on front end','acf'), 223 | 'type' => 'radio', 224 | 'name' => 'save_format', 225 | 'layout' => 'horizontal', 226 | 'class' => 'return-value-select', 227 | 'choices' => array( 228 | 'object' => __("Image Array",'acf'), 229 | 'url' => __("Image URL",'acf'), 230 | 'id' => __("Image ID",'acf') 231 | ) 232 | )); 233 | 234 | // library 235 | acf_render_field_setting( $field, array( 236 | 'label' => __('Library','acf'), 237 | 'instructions' => __('Limit the media library choice','acf'), 238 | 'type' => 'radio', 239 | 'name' => 'library', 240 | 'layout' => 'horizontal', 241 | 'choices' => array( 242 | 'all' => __('All', 'acf'), 243 | 'uploadedTo' => __('Uploaded to post', 'acf') 244 | ) 245 | )); 246 | 247 | } 248 | 249 | 250 | 251 | /* 252 | * render_field() 253 | * 254 | * Create the HTML interface for your field 255 | * 256 | * @param $field (array) the $field being rendered 257 | * 258 | * @type action 259 | * @since 3.6 260 | * @date 23/01/13 261 | * 262 | * @param $field (array) the $field being edited 263 | * @return n/a 264 | */ 265 | 266 | function render_field( $field ) { 267 | 268 | 269 | // enqueue 270 | acf_enqueue_uploader(); 271 | 272 | // get data from value 273 | //$data = json_decode($field['value']); 274 | $imageData = $this->get_image_data($field); 275 | 276 | $url = ''; 277 | $orignialImage = null; 278 | 279 | if($imageData->original_image){ 280 | $originalImage = wp_get_attachment_image_src($imageData->original_image, 'full'); 281 | $url = $imageData->preview_image_url; 282 | } 283 | 284 | $width = 0; 285 | $height = 0; 286 | 287 | if($field['target_size'] == 'custom'){ 288 | $width = $field['width']; 289 | $height = $field['height']; 290 | } 291 | else{ 292 | global $_wp_additional_image_sizes; 293 | $s = $field['target_size']; 294 | if (isset($_wp_additional_image_sizes[$s])) { 295 | $width = intval($_wp_additional_image_sizes[$s]['width']); 296 | $height = intval($_wp_additional_image_sizes[$s]['height']); 297 | } else { 298 | $width = get_option($s.'_size_w'); 299 | $height = get_option($s.'_size_h'); 300 | } 301 | } 302 | 303 | // Retina mode 304 | if($this->getOption('retina_mode') || $field['retina_mode'] == 'yes'){ 305 | $width = $width * 2; 306 | $height = $height * 2; 307 | } 308 | 309 | // vars 310 | $div_atts = array( 311 | 'class' => 'acf-image-uploader acf-cf acf-image-crop', 312 | 'data-field-settings' => json_encode($field), 313 | 'data-crop_type' => $field['crop_type'], 314 | 'data-target_size' => $field['target_size'], 315 | 'data-width' => $width, 316 | 'data-height' => $height, 317 | 'data-force_crop' => $field['force_crop'] == 'yes' ? 'true' : 'false', 318 | 'data-save_to_media_library' => $field['save_in_media_library'], 319 | 'data-save_format' => $field['save_format'], 320 | 'data-preview_size' => $field['preview_size'], 321 | 'data-library' => $field['library'] 322 | ); 323 | $input_atts = array( 324 | 'type' => 'hidden', 325 | 'name' => $field['name'], 326 | 'value' => htmlspecialchars($field['value']), 327 | 'data-name' => 'id', 328 | 'data-original-image' => $imageData->original_image, 329 | 'data-cropped-image' => json_encode($imageData->cropped_image), 330 | 'class' => 'acf-image-value' 331 | ); 332 | 333 | // has value? 334 | if($imageData->original_image){ 335 | $url = $imageData->preview_image_url; 336 | $div_atts['class'] .= ' has-value'; 337 | } 338 | 339 | ?> 340 |
> 341 |
342 | /> 343 |
344 |
345 |
    346 |
  • 347 |
  • 348 |
349 | 350 |
351 |
352 |
353 |

354 | original_image ): ?> 355 | 356 | 357 |
358 |
359 |

360 |
361 |
362 | 363 |
364 |
365 |
366 | 367 |
368 |
369 |
370 |

371 |
372 |
373 | original_image = ''; 383 | $imageData->original_image_width = ''; 384 | $imageData->original_image_height = ''; 385 | $imageData->cropped_image = ''; 386 | $imageData->original_image_url = ''; 387 | $imageData->preview_image_url = ''; 388 | $imageData->image_url = ''; 389 | 390 | if($field['value'] == ''){ 391 | // Field has not yet been saved or is an empty image field 392 | return $imageData; 393 | } 394 | 395 | $data = json_decode($field['value']); 396 | 397 | if(! is_object($data)){ 398 | // Field was saved as a regular image field 399 | $imageAtts = wp_get_attachment_image_src($field['value'], 'full'); 400 | $imageData->original_image = $field['value']; 401 | $imageData->original_image_width = $imageAtts[1]; 402 | $imageData->original_image_height = $imageAtts[2]; 403 | $imageData->preview_image_url = $this->get_image_src($field['value'], $field['preview_size']); 404 | $imageData->image_url = $this->get_image_src($field['value'], 'full'); 405 | $imageData->original_image_url = $imageData->image_url; 406 | return $imageData; 407 | } 408 | 409 | if( !is_numeric($data->original_image) ) 410 | { 411 | // The field has been saved, but has no image 412 | return $imageData; 413 | } 414 | 415 | // By now, we have at least a saved original image 416 | $imageAtts = wp_get_attachment_image_src($data->original_image, 'full'); 417 | $imageData->original_image = $data->original_image; 418 | $imageData->original_image_width = $imageAtts[1]; 419 | $imageData->original_image_height = $imageAtts[2]; 420 | $imageData->original_image_url = $this->get_image_src($data->original_image, 'full'); 421 | 422 | // Set defaults to original image 423 | $imageData->image_url = $this->get_image_src($data->original_image, 'full'); 424 | $imageData->preview_image_url = $this->get_image_src($data->original_image, $field['preview_size']); 425 | 426 | // Check if there is a cropped version and set appropriate attributes 427 | if(is_numeric($data->cropped_image)){ 428 | // Cropped image was saved to media library ans has an ID 429 | $imageData->cropped_image = $data->cropped_image; 430 | $imageData->image_url = $this->get_image_src($data->cropped_image, 'full'); 431 | $imageData->preview_image_url = $this->get_image_src($data->cropped_image, $field['preview_size']); 432 | } 433 | elseif(is_object($data->cropped_image)){ 434 | // Cropped image was not saved to media library and is only stored by its URL 435 | $imageData->cropped_image = $data->cropped_image; 436 | 437 | // Generate appropriate URLs 438 | $mediaDir = wp_upload_dir(); 439 | $imageData->image_url = $mediaDir['baseurl'] . '/' . $data->cropped_image->image; 440 | $imageData->preview_image_url = $mediaDir['baseurl'] . '/' . $data->cropped_image->preview; 441 | } 442 | return $imageData; 443 | } 444 | 445 | 446 | /* 447 | * input_admin_enqueue_scripts() 448 | * 449 | * This action is called in the admin_enqueue_scripts action on the edit screen where your field is created. 450 | * Use this action to add CSS + JavaScript to assist your render_field() action. 451 | * 452 | * @type action (admin_enqueue_scripts) 453 | * @since 3.6 454 | * @date 23/01/13 455 | * 456 | * @param n/a 457 | * @return n/a 458 | */ 459 | 460 | 461 | 462 | function input_admin_enqueue_scripts() { 463 | $dir = plugin_dir_url( __FILE__ ); 464 | 465 | 466 | // // register & include JS 467 | // wp_register_script( 'acf-input-image_crop', "{$dir}js/input.js" ); 468 | // wp_enqueue_script('acf-input-image_crop'); 469 | 470 | 471 | // // register & include CSS 472 | // wp_register_style( 'acf-input-image_crop', "{$dir}css/input.css" ); 473 | // wp_enqueue_style('acf-input-image_crop'); 474 | 475 | // register acf scripts 476 | //wp_register_script('acf-input-image', "{$dir}../advanced-custom-fields-pro/js/input/image.js"); 477 | wp_register_script('acf-input-image_crop', "{$dir}js/input.js", array('acf-input', 'imgareaselect')); 478 | 479 | wp_register_style('acf-input-image_crop', "{$dir}css/input.css", array('acf-input')); 480 | 481 | // scripts 482 | wp_enqueue_script(array( 483 | 'acf-input-image_crop' 484 | )); 485 | 486 | //wp_localize_script( 'acf-input-image_crop', 'ajax', array('nonce' => wp_create_nonce('acf_nonce')) ); 487 | 488 | // styles 489 | wp_enqueue_style(array( 490 | 'acf-input-image_crop', 491 | 'imgareaselect' 492 | )); 493 | 494 | 495 | } 496 | 497 | function perform_crop(){ 498 | $targetWidth = $_POST['target_width']; 499 | $targetHeight = $_POST['target_height']; 500 | 501 | $saveToMediaLibrary = isset($_POST['save_to_media_library']) && $_POST['save_to_media_library'] == 'yes'; 502 | $imageData = $this->generate_cropped_image($_POST['id'], $_POST['x1'], $_POST['x2'], $_POST['y1'], $_POST['y2'], $targetWidth, $targetHeight, $saveToMediaLibrary, $_POST['preview_size']); 503 | // $previewUrl = wp_get_attachment_image_src( $id, $_POST['preview_size']); 504 | // $fullUrl = wp_get_attachment_image_src( $id, 'full'); 505 | echo json_encode($imageData); 506 | die(); 507 | } 508 | 509 | function generate_cropped_image($id, $x1, $x2, $y1, $y2, $targetW, $targetH, $saveToMediaLibrary, $previewSize){//$id, $x1, $x2, $y$, $y2, $targetW, $targetH){ 510 | require_once ABSPATH . "/wp-admin/includes/file.php"; 511 | require_once ABSPATH . "/wp-admin/includes/image.php"; 512 | 513 | // Create the variable that will hold the new image data 514 | $imageData = array(); 515 | 516 | // Fetch media library info 517 | $mediaDir = wp_upload_dir(); 518 | 519 | // Get original image info 520 | $originalImageData = wp_get_attachment_metadata($id); 521 | 522 | // Get image editor from original image path to crop the image 523 | $image = wp_get_image_editor( $mediaDir['basedir'] . '/' . $originalImageData['file'] ); 524 | 525 | // Set quality 526 | $image->set_quality( apply_filters('acf-image-crop/image-quality', 100) ); 527 | 528 | if(! is_wp_error( $image ) ){ 529 | 530 | // Crop the image using the provided measurements 531 | $image->crop($x1, $y1, $x2 - $x1, $y2 - $y1, $targetW, $targetH); 532 | 533 | // Retrieve original filename and seperate it from its file extension 534 | $originalFileName = explode('.', basename($originalImageData['file'])); 535 | 536 | // Retrieve and remove file extension from array 537 | $originalFileExtension = array_pop($originalFileName); 538 | 539 | // Generate new base filename 540 | $targetFileName = implode('.', $originalFileName) . '_' . $targetW . 'x' . $targetH . apply_filters('acf-image-crop/filename_postfix', '_acf_cropped') . '.' . $originalFileExtension; 541 | 542 | 543 | // Generate target path new file using existing media library 544 | $targetFilePath = $mediaDir['path'] . '/' . wp_unique_filename( $mediaDir['path'], $targetFileName); 545 | 546 | // Get the relative path to save as the actual image url 547 | $targetRelativePath = str_replace($mediaDir['basedir'] . '/', '', $targetFilePath); 548 | 549 | // Save the image to the target path 550 | if(is_wp_error($image->save($targetFilePath))){ 551 | // There was an error saving the image 552 | //TODO handle it 553 | } 554 | 555 | // If file should be saved to media library, create an attachment for it at get the new attachment ID 556 | if($saveToMediaLibrary){ 557 | // Generate attachment from created file 558 | 559 | // Get the filetype 560 | $wp_filetype = wp_check_filetype(basename($targetFilePath), null ); 561 | $attachment = array( 562 | 'guid' => $mediaDir['url'] . '/' . basename($targetFilePath), 563 | 'post_mime_type' => $wp_filetype['type'], 564 | 'post_title' => preg_replace('/\.[^.]+$/', '', basename($targetFilePath)), 565 | 'post_content' => '', 566 | 'post_status' => 'inherit' 567 | ); 568 | $attachmentId = wp_insert_attachment( $attachment, $targetFilePath); 569 | $attachmentData = wp_generate_attachment_metadata( $attachmentId, $targetFilePath ); 570 | wp_update_attachment_metadata( $attachmentId, $attachmentData ); 571 | add_post_meta($attachmentId, 'acf_is_cropped', 'true', true); 572 | 573 | // Add the id to the imageData-array 574 | $imageData['value'] = $attachmentId; 575 | 576 | // Add the image url 577 | $imageUrlObject = wp_get_attachment_image_src( $attachmentId, 'full'); 578 | $imageData['url'] = $imageUrlObject[0]; 579 | 580 | // Add the preview url as well 581 | $previewUrlObject = wp_get_attachment_image_src( $attachmentId, $previewSize); 582 | $imageData['preview_url'] = $previewUrlObject[0]; 583 | } 584 | // Else we need to return the actual path of the cropped image 585 | else{ 586 | // Add the image url to the imageData-array 587 | $imageData['value'] = array('image' => $targetRelativePath); 588 | $imageData['url'] = $mediaDir['baseurl'] . '/' . $targetRelativePath; 589 | 590 | // Get preview size dimensions 591 | global $_wp_additional_image_sizes; 592 | $previewWidth = 0; 593 | $previewHeight = 0; 594 | $crop = 0; 595 | if (isset($_wp_additional_image_sizes[$previewSize])) { 596 | $previewWidth = intval($_wp_additional_image_sizes[$previewSize]['width']); 597 | $previewHeight = intval($_wp_additional_image_sizes[$previewSize]['height']); 598 | $crop = $_wp_additional_image_sizes[$previewSize]['crop']; 599 | } else { 600 | $previewWidth = get_option($previewSize.'_size_w'); 601 | $previewHeight = get_option($previewSize.'_size_h'); 602 | $crop = get_option($previewSize.'_crop'); 603 | } 604 | 605 | // Generate preview file path 606 | $previewFilePath = $mediaDir['path'] . '/' . wp_unique_filename( $mediaDir['path'], 'preview_' . $targetFileName); 607 | $previewRelativePath = str_replace($mediaDir['basedir'] . '/', '', $previewFilePath); 608 | 609 | // Get image editor from cropped image 610 | $croppedImage = wp_get_image_editor( $targetFilePath ); 611 | $croppedImage->resize($previewWidth, $previewHeight, $crop); 612 | 613 | // Save the preview 614 | $croppedImage->save($previewFilePath); 615 | 616 | // Add the preview url 617 | $imageData['preview_url'] = $mediaDir['baseurl'] . '/' . $previewRelativePath; 618 | $imageData['value']['preview'] = $previewRelativePath; 619 | } 620 | $imageData['success'] = true; 621 | return $imageData; 622 | } 623 | else{ 624 | // Handle WP_ERROR 625 | $response = array(); 626 | $response['success'] = false; 627 | $response['error_message'] = ''; 628 | foreach($image->error_data as $code => $message){ 629 | $response['error_message'] .= '

' . $code . ': ' . $message . '

'; 630 | } 631 | return $response; 632 | } 633 | } 634 | 635 | function get_image_src($id, $size = 'thumbnail'){ 636 | $atts = wp_get_attachment_image_src( $id, $size); 637 | return $atts[0]; 638 | } 639 | 640 | function getAbsoluteImageUrl($relativeUrl){ 641 | $mediaDir = wp_upload_dir(); 642 | return $mediaDir['baseurl'] . '/' . $relativeUrl; 643 | } 644 | 645 | function getImagePath($relativePath){ 646 | $mediaDir = wp_upload_dir(); 647 | return $mediaDir['basedir'] . '/' . $relativePath; 648 | } 649 | 650 | function filterMediaQuery($args){ 651 | // get options 652 | $options = get_option( 'acf_image_crop_settings' ); 653 | $hide = ( isset($options['hide_cropped']) && $options['hide_cropped'] ); 654 | 655 | // If hide option is enabled, do not select items with the acf_is_cropped meta-field 656 | if($hide){ 657 | $args['meta_query']= array( 658 | array( 659 | 'key' => 'acf_is_cropped', 660 | 'compare' => 'NOT EXISTS' 661 | ) 662 | ); 663 | } 664 | return $args; 665 | } 666 | 667 | 668 | function registerSettings(){ 669 | add_settings_section( 670 | 'acf_image_crop_settings', 671 | __('ACF Image Crop Settings','acf-image_crop'), 672 | array($this, 'displayImageCropSettingsSection'), 673 | 'media' 674 | ); 675 | 676 | register_setting( 677 | 'media', // settings page 678 | 'acf_image_crop_settings', // option name 679 | // added 680 | // a callback to a new function to set options to false and not to an empty string 681 | // there was an error if you set an option, save and then unset this option and save again 682 | // because the option value could be empty if non of the options is activated 683 | array($this, 'validateImageCropSettingsSection') 684 | // added END 685 | ); 686 | 687 | add_settings_field( 688 | 'acf_image_crop_hide_cropped', // id 689 | __('Hide cropped images from media dialog', 'acf-image_crop'), // setting title 690 | array($this, 'displayHideFromMediaInput'), // display callback 691 | 'media', // settings page 692 | 'acf_image_crop_settings' // settings section 693 | ); 694 | 695 | add_settings_field( 696 | 'acf_image_crop_retina_mode', // id 697 | __('Enable global retina mode (beta)', 'acf-image_crop'), // setting title 698 | array($this, 'displayRetinaModeInput'), // display callback 699 | 'media', // settings page 700 | 'acf_image_crop_settings' // settings section 701 | ); 702 | } 703 | 704 | function displayHideFromMediaInput(){ 705 | // Get plugin options 706 | $options = get_option( 'acf_image_crop_settings' ); 707 | $value = $options['hide_cropped']; 708 | 709 | // echo the field 710 | ?> 711 | value='true' /> 713 | 723 | value='true' /> 725 | cropped_image; 943 | } 944 | else{ 945 | // We are migrating from a standard image field 946 | $data = new stdClass(); 947 | $data->cropped_image = $value; 948 | $data->original_image = $value; 949 | } 950 | 951 | // format 952 | if( $field['save_format'] == 'url' ) 953 | { 954 | if(is_numeric($data->cropped_image)){ 955 | $value = wp_get_attachment_url( $data->cropped_image ); 956 | } 957 | elseif(is_array($data->cropped_image)){ 958 | 959 | $value = $this->getAbsoluteImageUrl($data->cropped_image['image']); 960 | } 961 | elseif(is_object($data->cropped_image)){ 962 | $value = $this->getAbsoluteImageUrl($data->cropped_image->image); 963 | } 964 | 965 | } 966 | elseif( $field['save_format'] == 'object' ) 967 | { 968 | if(is_numeric($data->cropped_image )){ 969 | $value = $this->getImageArray($data->cropped_image); 970 | $value['original_image'] = $this->getImageArray($data->original_image); 971 | // $attachment = get_post( $data->cropped_image ); 972 | // // validate 973 | // if( !$attachment ) 974 | // { 975 | // return false; 976 | // } 977 | 978 | 979 | // // create array to hold value data 980 | // $src = wp_get_attachment_image_src( $attachment->ID, 'full' ); 981 | 982 | // $value = array( 983 | // 'id' => $attachment->ID, 984 | // 'alt' => get_post_meta($attachment->ID, '_wp_attachment_image_alt', true), 985 | // 'title' => $attachment->post_title, 986 | // 'caption' => $attachment->post_excerpt, 987 | // 'description' => $attachment->post_content, 988 | // 'mime_type' => $attachment->post_mime_type, 989 | // 'url' => $src[0], 990 | // 'width' => $src[1], 991 | // 'height' => $src[2], 992 | // 'sizes' => array(), 993 | // ); 994 | 995 | 996 | // // find all image sizes 997 | // $image_sizes = get_intermediate_image_sizes(); 998 | 999 | // if( $image_sizes ) 1000 | // { 1001 | // foreach( $image_sizes as $image_size ) 1002 | // { 1003 | // // find src 1004 | // $src = wp_get_attachment_image_src( $attachment->ID, $image_size ); 1005 | 1006 | // // add src 1007 | // $value[ 'sizes' ][ $image_size ] = $src[0]; 1008 | // $value[ 'sizes' ][ $image_size . '-width' ] = $src[1]; 1009 | // $value[ 'sizes' ][ $image_size . '-height' ] = $src[2]; 1010 | // } 1011 | // // foreach( $image_sizes as $image_size ) 1012 | // } 1013 | } 1014 | elseif(is_array( $data->cropped_image) || is_object($data->cropped_image)){ 1015 | // Cropped image is not saved to media directory. Get data from original image instead 1016 | $value = $this->getImageArray($data->original_image); 1017 | 1018 | // Get the relative url from data 1019 | $relativeUrl = ''; 1020 | if(is_array( $data->cropped_image)){ 1021 | $relativeUrl = $data->cropped_image['image']; 1022 | } 1023 | else{ 1024 | $relativeUrl = $data->cropped_image->image; 1025 | } 1026 | 1027 | // Replace URL with cropped version 1028 | $value['url'] = $this->getAbsoluteImageUrl($relativeUrl); 1029 | 1030 | // Calculate and replace sizes 1031 | $imagePath = $this->getImagePath($relativeUrl); 1032 | $dimensions = getimagesize($imagePath); 1033 | $value['width'] = $dimensions[0]; 1034 | $value['height'] = $dimensions[1]; 1035 | 1036 | // Add original image info 1037 | $value['original_image'] = $this->getImageArray($data->original_image); 1038 | } 1039 | // elseif(is_object($data->cropped_image)){ 1040 | // $value = $this->getImageArray($data->original_image); 1041 | // $value['url'] = $this->getAbsoluteImageUrl($data->cropped_image->image); 1042 | 1043 | // // Calculate sizes 1044 | // $imagePath = $this->getImagePath($data->cropped_image->image); 1045 | // $dimensions = getimagesize($imagePath); 1046 | // $value['width'] = $dimensions[0]; 1047 | // $value['height'] = $dimensions[1]; 1048 | 1049 | // // Add original image info 1050 | // $value['original_image'] = $this->getImageArray($data->original_image); 1051 | // } 1052 | else{ 1053 | //echo 'ELSE'; 1054 | } 1055 | 1056 | } 1057 | return $value; 1058 | 1059 | } 1060 | 1061 | function getOption($key){ 1062 | return isset($this->options[$key]) ? $this->options[$key] : null; 1063 | } 1064 | 1065 | function getImageArray($id){ 1066 | $attachment = get_post( $id ); 1067 | // validate 1068 | if( !$attachment ) 1069 | { 1070 | return false; 1071 | } 1072 | 1073 | 1074 | // create array to hold value data 1075 | $src = wp_get_attachment_image_src( $attachment->ID, 'full' ); 1076 | 1077 | $imageArray = array( 1078 | 'id' => $attachment->ID, 1079 | 'alt' => get_post_meta($attachment->ID, '_wp_attachment_image_alt', true), 1080 | 'title' => $attachment->post_title, 1081 | 'caption' => $attachment->post_excerpt, 1082 | 'description' => $attachment->post_content, 1083 | 'mime_type' => $attachment->post_mime_type, 1084 | 'url' => $src[0], 1085 | 'width' => $src[1], 1086 | 'height' => $src[2], 1087 | 'sizes' => array(), 1088 | ); 1089 | 1090 | 1091 | // find all image sizes 1092 | $image_sizes = get_intermediate_image_sizes(); 1093 | 1094 | if( $image_sizes ) 1095 | { 1096 | foreach( $image_sizes as $image_size ) 1097 | { 1098 | // find src 1099 | $src = wp_get_attachment_image_src( $attachment->ID, $image_size ); 1100 | 1101 | // add src 1102 | $imageArray[ 'sizes' ][ $image_size ] = $src[0]; 1103 | $imageArray[ 'sizes' ][ $image_size . '-width' ] = $src[1]; 1104 | $imageArray[ 'sizes' ][ $image_size . '-height' ] = $src[2]; 1105 | } 1106 | } 1107 | return $imageArray; 1108 | } 1109 | 1110 | 1111 | 1112 | /* 1113 | * validate_value() 1114 | * 1115 | * This filter is used to perform validation on the value prior to saving. 1116 | * All values are validated regardless of the field's required setting. This allows you to validate and return 1117 | * messages to the user if the value is not correct 1118 | * 1119 | * @type filter 1120 | * @date 11/02/2014 1121 | * @since 5.0.0 1122 | * 1123 | * @param $valid (boolean) validation status based on the value and the field's required setting 1124 | * @param $value (mixed) the $_POST value 1125 | * @param $field (array) the field array holding all the field options 1126 | * @param $input (string) the corresponding input name for $_POST value 1127 | * @return $valid 1128 | */ 1129 | 1130 | 1131 | 1132 | function validate_value( $valid, $value, $field, $input ){ 1133 | return $valid; 1134 | } 1135 | 1136 | 1137 | 1138 | 1139 | /* 1140 | * delete_value() 1141 | * 1142 | * This action is fired after a value has been deleted from the db. 1143 | * Please note that saving a blank value is treated as an update, not a delete 1144 | * 1145 | * @type action 1146 | * @date 6/03/2014 1147 | * @since 5.0.0 1148 | * 1149 | * @param $post_id (mixed) the $post_id from which the value was deleted 1150 | * @param $key (string) the $meta_key which the value was deleted 1151 | * @return n/a 1152 | */ 1153 | 1154 | /* 1155 | 1156 | function delete_value( $post_id, $key ) { 1157 | 1158 | 1159 | 1160 | } 1161 | 1162 | */ 1163 | 1164 | 1165 | /* 1166 | * load_field() 1167 | * 1168 | * This filter is applied to the $field after it is loaded from the database 1169 | * 1170 | * @type filter 1171 | * @date 23/01/2013 1172 | * @since 3.6.0 1173 | * 1174 | * @param $field (array) the field array holding all the field options 1175 | * @return $field 1176 | */ 1177 | 1178 | /* 1179 | 1180 | function load_field( $field ) { 1181 | 1182 | return $field; 1183 | 1184 | } 1185 | 1186 | */ 1187 | 1188 | 1189 | /* 1190 | * update_field() 1191 | * 1192 | * This filter is applied to the $field before it is saved to the database 1193 | * 1194 | * @type filter 1195 | * @date 23/01/2013 1196 | * @since 3.6.0 1197 | * 1198 | * @param $field (array) the field array holding all the field options 1199 | * @return $field 1200 | */ 1201 | 1202 | /* 1203 | 1204 | function update_field( $field ) { 1205 | 1206 | return $field; 1207 | 1208 | } 1209 | 1210 | */ 1211 | 1212 | 1213 | /* 1214 | * delete_field() 1215 | * 1216 | * This action is fired after a field is deleted from the database 1217 | * 1218 | * @type action 1219 | * @date 11/02/2014 1220 | * @since 5.0.0 1221 | * 1222 | * @param $field (array) the field array holding all the field options 1223 | * @return n/a 1224 | */ 1225 | 1226 | /* 1227 | 1228 | function delete_field( $field ) { 1229 | 1230 | 1231 | 1232 | } 1233 | 1234 | */ 1235 | 1236 | 1237 | } 1238 | 1239 | 1240 | // create field 1241 | new acf_field_image_crop(); 1242 | 1243 | ?> --------------------------------------------------------------------------------